def preprocess(self): self.target.mi.target = self.target # assemble hyper parameters self.length_scales, self.amp_variance, self.single_noise_variance, self.mean_noise_variance, self.precision_matrix = normscales.normscales( self.target.mi, self.devices, correlationsQ=self.correlationsQ) # build precision_matrix if not returned print('Precision before', self.precision_matrix) if self.precision_matrix is None: self.covarmat = np.diag(self.length_scales)**2 print('Covariance', self.covarmat) self.precision_matrix = np.linalg.inv(self.covarmat) print('Precision', self.precision_matrix) print('Length Scales', self.length_scales) # create OnlineGP model dim = len(self.devices) hyperparams = (self.precision_matrix, np.log(self.amp_variance), np.log(self.mean_noise_variance)) # self.model = OGP(dim, hyperparams, maxBV=self.numBV, weighted=False) self.model = OGP(dim, hyperparams, maxBV=self.numBV, covar=['RBF_ARD', 'MATERN32_ARD', 'MATERN52_ARD'][0], weighted=False) # initialize model on prior data if available if (self.prior_data is not None): p_X = self.prior_data.iloc[:, :-1] p_Y = self.prior_data.iloc[:, -1] num = p_X.shape[0] self.model.fit(p_X, p_Y, min(self.m, num)) # create Bayesian optimizer dev_ids = [dev.eid for dev in self.devices] dev_vals = [dev.get_value() for dev in self.devices] self.scanner = BayesOpt( model=self.model, target_func=self.target, acq_func=self.acq_func, xi=self.xi, alt_param=self.alt_param, m=self.m, bounds=self.bounds, iter_bound=self.iter_bound, prior_data=self.prior_data, start_dev_vals=dev_vals, dev_ids=dev_ids, searchBoundScaleFactor=self.searchBoundScaleFactor) self.scanner.max_iter = self.max_iter self.scanner.opt_ctrl = self.opt_ctrl
def __init__(self, dim, hidden_layers=[], dim_z=None, mask=None, alpha=1.0, noise=0.1, activations='lrelu', weight_dir=None): self.dim = dim self.dim_z = dim_z or dim # initialize the OGP object we use to actually make our predictions OGP_params = (np.zeros((self.dim_z, )), np.log(alpha), np.log(noise) ) # lengthscales of one (logged) self.ogp = OGP(self.dim_z, OGP_params) # our embedding function, initially the identity # if unchanged, the DKLGP should match the functionality of OGP self.embed = lambda x: x # build the neural network structure of the DKL self.layers = [] for l in hidden_layers: self.layers.append(Dense(l, activation=activations)) # add the linear output layer and the GP (used for likelihood training) if len(self.layers) > 0: self.layers.append(Dense(dim_z)) else: self.mask = mask self.layers.append(Dense(dim_z, mask=mask)) self.layers.append(CovMat( kernel='rbf', alpha_fixed=False)) # kernel should match the one used in OGP # if weight_dir is specified, we immediately initialize the embedding based on the specified neural network if weight_dir is not None: self.load_embedding(weight_dir)
def testFunc(): """ Function to try and find a bug in the model load function """ numBV = 50 xi = .01 bnds = None thing = GpScanner() pvs = [ "SIOC:SYS0:ML00:CALCOUT000", "SIOC:SYS0:ML00:CALCOUT999", "SIOC:SYS0:ML00:CALCOUT998", "SIOC:SYS0:ML00:CALCOUT997" ] #thing.setup(pvs, 'GDET:FEE1:241:ENRCHSTBR') thing.pvs = pvs thing.objective_func_pv = 'GDET:FEE1:241:ENRCHSTBR' total_delay = .2 mi = LCLSMachineInterface() mi.setUpDetector(pvs, detector=thing.objective_func_pv) mi.setup_data_record(pvs) dp = LCLSDeviceProperties() dp.get_start_values(pvs) interface = GpInterfaceWrapper(pvs, mi, dp, total_delay) s_data = thing.loadSeedData(thing.seed_file) hyps = thing.loadHyperParams(thing.hyp_file) thing.model = OGP(len(pvs), hyps, maxBV=50) filename = '/u1/lcls/matlab/data/2016/2016-04/2016-04-25/OcelotScan-2016-04-25-181811.mat' model_file = scipy.io.loadmat(filename)['data'] thing.model.alpha = model_file['alpha'].flatten(0)[0] thing.model.C = model_file['C'].flatten(0)[0] thing.model.BV = model_file['BV'].flatten(0)[0] thing.model.covar_params = model_file['covar_params'].flatten(0)[0] thing.model.KB = model_file['KB'].flatten(0)[0] thing.model.KBinv = model_file['KBinv'].flatten(0)[0] thing.model.weighted = model_file['weighted'].flatten(0)[0] thing.model.covar_params = (thing.model.covar_params[0][0], thing.model.covar_params[1][0]) print type(thing.model.covar_params) print thing.model.covar_params thing.model.predict(np.array(s_data[0, :-1], ndmin=2)) thing.opt = BOpt.BayesOpt(thing.model, interface, prior_data=pd.DataFrame(s_data))
def preprocess(self): hyp_params = HyperParams(pvs=self.devices, filename=self.hyper_file) self.energy = str(round(self.target.get_energy())) dev_ids = [dev.eid for dev in self.devices] hyps1 = hyp_params.loadHyperParams(self.hyper_file, self.energy, self.target, dev_ids, self.multiplier) dim = len(self.devices) self.model = OGP(dim, hyps1, maxBV=self.numBV, weighted=False) self.scanner = BayesOpt(model=self.model, target_func=self.target, acq_func=self.acq_func, xi=self.xi, alt_param=self.alt_param, m=self.m, bounds=self.bounds, iter_bound=self.iter_bound, prior_data=self.prior_data) self.scanner.max_iter = self.max_iter
class GaussProcess(Minimizer): def __init__(self, correlationsQ=False, searchBoundScaleFactor=None): super(GaussProcess, self).__init__() self.seed_timeout = 1 self.target = None self.devices = [] self.energy = 4 self.seed_iter = 0 self.numBV = 30 self.xi = 0.01 self.bounds = None self.acq_func = ['PI', 'EI', 'UCB'][-1] self.alt_param = -1 self.m = 200 self.iter_bound = False # filepath = os.path.join(os.getcwd(), "parameters", "hyperparameters.npy") # print('MINT-->Grabbing hyps from...: ', filepath) # self.hyper_file = filepath self.max_iter = 50 self.norm_coef = 0.1 self.multiplier = 1 self.simQ = False self.seedScanBool = True self.prior_data = None self.correlationsQ = correlationsQ self.searchBoundScaleFactor = searchBoundScaleFactor def seed_simplex(self): opt_smx = Optimizer() opt_smx.normalization = True opt_smx.norm_coef = self.norm_coef opt_smx.timeout = self.seed_timeout minimizer = Simplex() minimizer.max_iter = self.seed_iter opt_smx.minimizer = minimizer # opt.debug = True seq = [ Action(func=opt_smx.max_target_func, args=[self.target, self.devices]) ] opt_smx.eval(seq) seed_data = np.append( np.vstack(opt_smx.opt_ctrl.dev_sets), np.transpose(-np.array([opt_smx.opt_ctrl.penalty])), axis=1) import pandas as pd self.prior_data = pd.DataFrame(seed_data) self.seed_y_data = opt_smx.opt_ctrl.penalty def preprocess(self): self.target.mi.target = self.target # assemble hyper parameters self.length_scales, self.amp_variance, self.single_noise_variance, self.mean_noise_variance, self.precision_matrix = normscales.normscales( self.target.mi, self.devices, correlationsQ=self.correlationsQ) # build precision_matrix if not returned print('Precision before', self.precision_matrix) if self.precision_matrix is None: self.covarmat = np.diag(self.length_scales)**2 print('Covariance', self.covarmat) self.precision_matrix = np.linalg.inv(self.covarmat) print('Precision', self.precision_matrix) print('Length Scales', self.length_scales) # create OnlineGP model dim = len(self.devices) hyperparams = (self.precision_matrix, np.log(self.amp_variance), np.log(self.mean_noise_variance)) # self.model = OGP(dim, hyperparams, maxBV=self.numBV, weighted=False) self.model = OGP(dim, hyperparams, maxBV=self.numBV, covar=['RBF_ARD', 'MATERN32_ARD', 'MATERN52_ARD'][0], weighted=False) # initialize model on prior data if available if (self.prior_data is not None): p_X = self.prior_data.iloc[:, :-1] p_Y = self.prior_data.iloc[:, -1] num = p_X.shape[0] self.model.fit(p_X, p_Y, min(self.m, num)) # create Bayesian optimizer dev_ids = [dev.eid for dev in self.devices] dev_vals = [dev.get_value() for dev in self.devices] self.scanner = BayesOpt( model=self.model, target_func=self.target, acq_func=self.acq_func, xi=self.xi, alt_param=self.alt_param, m=self.m, bounds=self.bounds, iter_bound=self.iter_bound, prior_data=self.prior_data, start_dev_vals=dev_vals, dev_ids=dev_ids, searchBoundScaleFactor=self.searchBoundScaleFactor) self.scanner.max_iter = self.max_iter self.scanner.opt_ctrl = self.opt_ctrl def minimize(self, error_func, x): self.energy = self.mi.get_energy() if self.seedScanBool: self.seed_simplex() self.preprocess() # x = [dev.get_value() for dev in self.devices] # is this needed? self.scanner.minimize(error_func, x) self.saveModel() return def saveModel(self): """ Add GP model parameters to the save file. """ # add in extra GP model data to save try: self.mi.data except: self.mi.data = {} self.mi.data["acq_fcn"] = self.acq_func # OnlineGP stuff try: self.mi.data["alpha"] = self.model.alpha except: pass try: self.mi.data["C"] = self.model.C except: pass try: self.mi.data["BV"] = self.model.BV except: pass try: self.mi.data["covar_params"] = self.model.covar_params except: pass try: self.mi.data["KB"] = self.model.KB except: pass try: self.mi.data["KBinv"] = self.model.KBinv except: pass try: self.mi.data["weighted"] = self.model.weighted except: pass try: self.mi.data["noise_var"] = self.model.noise_var except: pass try: self.mi.data["corrmat"] = self.corrmat except: pass try: self.mi.data["covarmat"] = self.covarmat except: pass try: self.mi.data["length_scales"] = self.length_scales except: pass try: self.mi.data["amp_variance"] = self.amp_variance except: pass try: self.mi.data["single_noise_variance"] = self.single_noise_variance except: pass try: self.mi.data["mean_noise_variance"] = self.mean_noise_variance except: pass try: self.mi.data["precision_matrix"] = self.precision_matrix except: pass self.mi.data["seedScanBool"] = self.seedScanBool if self.seedScanBool: self.mi.data["nseed"] = self.prior_data.shape[0] else: self.mi.data["nseed"] = 0 if type(self.model.prmeanp) is type(None): self.mi.data["prmean_params_amp"] = "None" self.mi.data["prmean_params_centroid"] = "None" self.mi.data["prmean_params_invcovarmat"] = "None" else: self.mi.data["prmean_params_amp"] = self.model.prmeanp[0] self.mi.data["prmean_params_centroid"] = self.model.prmeanp[1] self.mi.data["prmean_params_invcovarmat"] = self.model.prmeanp[2] if type(self.model.prvarp) is type(None): self.mi.data["prvar_params"] = "None" else: self.mi.data["prvar_params"] = self.model.prvarp try: self.mi.data["prmean_name"] = self.model.prmean_name except: pass try: self.mi.data["prior_pv_info"] = self.model.prior_pv_info except: pass
def test_GP(): """ test GP method :return: """ def get_limits(): return [-100, 100] d1 = TestDevice(eid="d1") d1.get_limits = get_limits d2 = TestDevice(eid="d2") d2.get_limits = get_limits d3 = TestDevice(eid="d3") d3.get_limits = get_limits devices = [d1, d2] target = TestTarget() opt = Optimizer() opt.timeout = 0 opt_smx = Optimizer() opt_smx.timeout = 0 minimizer = Simplex() minimizer.max_iter = 3 opt_smx.minimizer = minimizer #opt.debug = True seq = [Action(func=opt_smx.max_target_func, args=[target, devices])] opt_smx.eval(seq) s_data = np.append(np.vstack(opt_smx.opt_ctrl.dev_sets), np.transpose(-np.array([opt_smx.opt_ctrl.penalty])), axis=1) print(s_data) # -------------- GP config setup -------------- # #GP parameters numBV = 30 xi = 0.01 #no input bounds on GP selection for now pvs = [dev.eid for dev in devices] hyp_params = HyperParams(pvs=pvs, filename="../parameters/hyperparameters.npy") ave = np.mean(-np.array(opt_smx.opt_ctrl.penalty)) std = np.std(-np.array(opt_smx.opt_ctrl.penalty)) noise = hyp_params.calcNoiseHP(ave, std=0.) coeff = hyp_params.calcAmpCoeffHP(ave, std=0.) len_sc_hyps = [] for dev in devices: ave = 10 std = 3 len_sc_hyps.append(hyp_params.calcLengthScaleHP(ave, std)) print("y_data", opt_smx.opt_ctrl.penalty) print("pd.DataFrame(s_data)", pd.DataFrame(s_data)) print("len_sc_hyps", len_sc_hyps) bnds = None #hyps = hyp_params.loadHyperParams(energy=3, detector_stat_params=target.get_stat_params()) hyps1 = (np.array([len_sc_hyps]), coeff, noise ) #(np.array([hyps]), coeff, noise) print("hyps1", hyps1) #exit(0) #init model dim = len(pvs) model = OGP(dim, hyps1, maxBV=numBV, weighted=False) minimizer = BayesOpt(model, target_func=target, xi=0.01, acq_func='EI', bounds=bnds, prior_data=pd.DataFrame(s_data)) minimizer.devices = devices minimizer.max_iter = 300 opt.minimizer = minimizer seq = [Action(func=opt.max_target_func, args=[target, devices])] opt.eval(seq)
class DKLGP(object): def __init__(self, dim, hidden_layers=[], dim_z=None, mask=None, alpha=1.0, noise=0.1, activations='lrelu', weight_dir=None): self.dim = dim self.dim_z = dim_z or dim # initialize the OGP object we use to actually make our predictions OGP_params = (np.zeros((self.dim_z, )), np.log(alpha), np.log(noise) ) # lengthscales of one (logged) self.ogp = OGP(self.dim_z, OGP_params) # our embedding function, initially the identity # if unchanged, the DKLGP should match the functionality of OGP self.embed = lambda x: x # build the neural network structure of the DKL self.layers = [] for l in hidden_layers: self.layers.append(Dense(l, activation=activations)) # add the linear output layer and the GP (used for likelihood training) if len(self.layers) > 0: self.layers.append(Dense(dim_z)) else: self.mask = mask self.layers.append(Dense(dim_z, mask=mask)) self.layers.append(CovMat( kernel='rbf', alpha_fixed=False)) # kernel should match the one used in OGP # if weight_dir is specified, we immediately initialize the embedding based on the specified neural network if weight_dir is not None: self.load_embedding(weight_dir) # sets up the DKL and trains the embedding. nullifies the effect of load_embedding if it was called previously # lr is the learning rate: reasonable deafult is 2e-4 # maxiter is the number of iterations of the solver; scales the training time linearly # batch_size is the size of a mini batch; scales the training time ~quadratically # gp = True in NNRegressor() sets gp likelihood as optimization target def train_embedding(self, x, y, lr=2.e-4, batch_size=50, maxiter=4000): opt = Adam(lr) self.DKLmodel = NNRegressor(self.layers, opt=opt, batch_size=batch_size, maxiter=maxiter, gp=True, verbose=False) self.DKLmodel.fit(x, y) self.embed = self.DKLmodel.fast_forward # fast_forward gives mapping up to (but not including) gp (x -> z) # (something like) full_forward maps through the whole dkl + gp # loads the DKL and embedding from the specified directory. forgets any previous embedding # note that network structure and activations, etc. still need to be specified in __init__ def load_embedding(self, dname): self.DKLmodel = NNRegressor(self.layers) self.DKLmodel.first_run(np.zeros((1, self.dim)), None, load_path=dname) self.embed = self.DKLmodel.fast_forward # saves the neural network parameters to specified directory, allowing the saved embedding to be replicated without re-training it def save_embedding(self, dname): if not os.path.isdir(dname): os.makedirs(dname) self.DKLmodel.save_weights(dname) # allows manually setting a linear transform. Make sure you get your tranpose stuff right (x_rows.shape is [npoints,ndim]) def set_linear(self, matrix): self.linear_transform = matrix self.embed = lambda x_rows: np.dot(x_rows, self.linear_transform) # sets a linear transformation based on a given correlation matrix which is assumed to fit the data # NOTE: this isn't necessarily log-likelihood-optimal def linear_from_correlation(self, matrix): # multinormal covariance matrix center = np.linalg.inv(matrix) chol = np.linalg.cholesky(center) self.set_linear(chol) # computes the log-likelihood of the given data set using the current embedding # ASSUMES YOU'RE USING RBF KERNEL def eval_LL(self, X, Y): N = X.shape[0] Z = self.embed(X) diffs = euclidean_distances(Z, squared=True) alpha = np.exp(self.ogp.covar_params[1]) # kind of a hack rbf_K = alpha * np.exp(-diffs / 2.) K_full = rbf_K + (self.ogp.noise_var) * np.eye(N) L = np.linalg.cholesky(K_full) # K = L * L.T Ly = np.linalg.solve(L, Y) # finds inverse(L) * y log_lik = -0.5 * np.sum(Ly**2) # -1/2 * y.T * inverse(L * L.T) * y log_lik -= np.sum(np.log( np.diag(L))) # equivalent to -1/2 * log(det(K)) log_lik -= 0.5 * N * np.log(2 * np.pi) return float(log_lik) # allows passing custom alpha/noise # if compute_deriv is true, assumes that embedding is linear and returns derivative w.r.t. transform def custom_LL(self, X, Y, alpha, noise_variance, compute_deriv=False): N, dim = X.shape Z = self.embed(X) if not compute_deriv: diffs = euclidean_distances(Z, squared=True) rbf_K = alpha * np.exp(-diffs / 2.) K_full = rbf_K + noise_variance * np.eye(N) L = np.linalg.cholesky(K_full) # K = L * L.T Ly = np.linalg.solve(L, Y) # finds inverse(L) * y log_lik = -0.5 * np.sum(Ly**2) # -1/2 * y.T * inverse(L * L.T) * y log_lik -= np.sum(np.log( np.diag(L))) # equivalent to -1/2 * log(det(K)) log_lik -= 0.5 * N * np.log(2 * np.pi) return float(log_lik) lengths = [0. for d in range(dim)] params = lengths + [np.log(alpha)] + [np.log(noise_variance)] neglik, deriv = SPGP_likelihood_4scipy(params, Y, Z) deriv_noise = deriv[-1] deriv_coeff = deriv[-2] deriv_z = deriv[:self.dim_z * N].reshape((N, self.dim_z)) deriv_transform = np.dot(X.T, deriv_z) mask = self.mask or np.ones((dim, dim_z)) return -neglik, deriv_transform * mask, deriv_coeff, deriv_noise # takes an n x dim_z matrix Z and translates it to x, assuming the embedding is linear # currently requires that the model embedding was set via set_linear def inverse_embed(self, Z): assert ('linear_transform' in dir(self)) transform = self.linear_transform # assumption is that z = x * transform column_x = np.linalg.solve(transform.T, Z.T) return column_x.T ########## # remaining functions mimic Online GP functionality, just embedding x -> z first ########## def fit(self, X, y): Z = self.embed(X) self.ogp.fit(Z, y) def update(self, x_new, y_new): z_new = self.embed(x_new) self.ogp.update(z_new, y_new) def predict(self, x): z = np.array(self.embed(x), ndmin=2) return self.ogp.predict(z)
def setup(self, devices, objective_func, iters=45): """ Basic setup procedure for the GP scan objects, input args that are common to optimizer classes. Similar setup funtion with the same args as the ocelot scanner. Does not contain option to load in MachineInterface and DeviceProperties Could probably just write all this into the init function if you have motivation. Args: devices (List[str]): List of the PVs to be used in scan objective_func (str): String for the PV for the value to maximize iters (int): Number of iterations for the scanner to run """ #load in the pvs to scan self.devices = devices self.objective_func = objective_func # for testing self.objective_func.devices = self.devices #number of iterations to scan (hardcode for now) self.iters = iters #set new timing variables #self.mi.secs_to_ave = self.parent.data_delay self.total_delay = self.parent.trim_delay + self.parent.data_delay # -------------- GP config setup -------------- # #GP parameters self.numBV = 30 self.xi = 0.01 #no input bounds on GP selection for now bnds = None pvs = [dev.eid for dev in devices] hyp_params = BOpt.HyperParams(pvs=pvs, filename=self.hyp_file) hyps = hyp_params.loadHyperParams( objective_func.get_energy(), detector_stat_params=objective_func.get_stat_params()) #init model dim = len(devices) self.model = OGP(dim, hyps, maxBV=self.numBV, weighted=False) #load model stuff filename = '/u1/lcls/matlab/data/2016/2016-04/2016-04-25/OcelotScan-2016-04-25-181811.mat' #if you would lake to load a model from file, use this function #self.model = self.loadModelParams(self.model,filename) #if this is not a simplex seeded scan, setup the seed data from mat file and build optimizer object\ if not self.seedScanBool: #load seed data file s_data = self.loadSeedData(self.seed_file) #optimzer object self.opt = Optimizer() self.opt.timeout = self.total_delay minimizer = BOpt.BayesOpt(self.model, self.objective_func, acq_func='EI', xi=self.xi, bounds=bnds, prior_data=pd.DataFrame(s_data)) #self.opt = BOpt.BayesOpt(self.model, self.interface, xi=self.xi, acq_func='EI', bounds=bnds, prior_data=pd.DataFrame(s_data)) #bool to kill scanner thread from GUI minimizer.devices = devices minimizer.max_iter = iter self.opt.minimizer = minimizer self.seq = [ Action(func=self.opt.max_target_func, args=[objective_func, self.devices]) ] self.opt.kill = False