class FlexiBO(object): """This class is used to implement an active learning approach to optimize multiple objectives of different cost E: design space O: evaluated objectives n: number of objectives m1: objective 1 m2: objective 2 """ def __init__(self, data): print("Initializing FlexiBO class") self.utils = Utils() self.sample = Sample() self.df = data cfg = ConfigReal() (self.E, self.O, self.measurement) = cfg.set_design_space() self.NUM_ITER = 200 self.NUM_OBJ = 2 self.O1_IND = 0 self.O2_IND = 1 self.m1 = "inference_time" self.m2 = "power_consumption" self.SM = SurrogateModel() (self.X, self.Y1, self.Y2) = self.prepare_training_data() self.fit_rf() self.perform_bo_loop() def fit_rf(self): """This function is used to predict """ self.rf1 = RandomForestRegressor(n_estimators=16) self.rf2 = RandomForestRegressor(n_estimators=16) self.rf1.fit(self.X, self.Y1) self.rf2.fit(self.X, self.Y2) def prepare_training_data(self): """This function is used to prepare training data """ X = self.df[['number_of_cores', 'core_freq', 'gpu_freq', 'emc_freq']].values Y1 = self.df[self.m1].values Y2 = self.df[self.m2].values Y1 = [[i] for i in Y1] Y2 = [[i] for i in Y2] return (X, Y1, Y2) def initialize(self): """This function is used to initialize data """ import random index = random.sample(range(0, len(self.X) - 1), 10) X = [self.X[i] for i in index] Y1 = [self.Y1[i] for i in index] Y2 = [self.Y2[i] for i in index] return (X, Y1, Y2, index) def perform_bo_loop(self): """This function is used to perform bayesian optimization loop U: Design Space REGION: Uncertainty Region for each configuration in design space """ # Initialization BETA = 1.0 (init_X, init_Y1, init_Y2, init_measured_indices) = self.initialize() for i in range(0, len(init_measured_indices)): self.O[i]["o1"] = True self.measurement[init_measured_indices[i]]["o1"] = init_Y1[i][0] self.O[i]["o2"] = True self.measurement[init_measured_indices[i]]["o2"] = init_Y2[i][0] (init_X, init_Y1, init_Y2) = (np.array(init_X), np.array(init_Y1), np.array(init_Y2)) U = np.array(self.E[:]) init_X1 = init_X[:] init_X2 = init_X[:] rbf = ConstantKernel(1.0) * RBF(length_scale=1.0) gpr = GaussianProcessRegressor(kernel=rbf, n_restarts_optimizer=9) # bo loop for iteration in range(0, self.NUM_ITER): print("---------------------------------------Iteration: ", iteration) REGION = [{} for _ in U] # Fit a GP for each objective #model_o1=self.SM.fit_gp(init_X1,init_Y1) #model_o2=self.SM.fit_gp(init_X2,init_Y2) model_o1 = gpr.fit(init_X1, init_Y1) model_o2 = gpr.fit(init_X2, init_Y2) for config in range(0, len(U)): # Compute mu and sigma of each points for each objective cur = np.array([U[config]]) cur_eval = self.O[config] # Objective 1 if cur_eval["o1"] is False: #(mu_o1,sigma_o1)=self.SM.get_gp_model_params(model_o1,cur) mu_o1, sigma_o1 = model_o1.predict(cur, return_std=True) else: (mu_o1, sigma_o1) = (self.measurement[config]["o1"], 0) # Objective 2 if cur_eval["o2"] is False: #(mu_o2,sigma_o2)=self.SM.get_gp_model_params(model_o2,cur) mu_o2, sigma_o2 = model_o2.predict(cur, return_std=True) else: (mu_o2, sigma_o2) = (self.measurement[config]["o2"], 0) # Compute uncertainty region for each point using mu and sigma REGION[config]["pes"] = [ 0 if (mu_o1 - math.sqrt(BETA) * sigma_o1) < 0 else mu_o1 - math.sqrt(BETA) * sigma_o1, 0 if (mu_o2 - math.sqrt(BETA) * sigma_o2) < 0 else mu_o2 - math.sqrt(BETA) * sigma_o2 ] REGION[config]["avg"] = [mu_o1, mu_o2] REGION[config]["opt"] = [ mu_o1 + math.sqrt(BETA) * sigma_o1, mu_o2 + math.sqrt(BETA) * sigma_o2 ] print(REGION) """ REGION=[ {'opt': [2, 11], 'avg': [1, 10], 'pes': [0.5,9]}, {'opt': [3, 9], 'avg': [2.5, 8], 'pes': [1.5,7]}, {'opt': [1.5, 7], 'avg': [1, 6], 'pes': [0.5,5]}, {'opt': [5, 6], 'avg': [4.5, 5], 'pes': [4,4]}, {'opt': [7.5, 4], 'avg': [6.5, 3], 'pes': [6,2.5]}, {'opt': [3.5, 3.5], 'avg': [3, 3], 'pes': [2.5,2.5]}, {'opt': [5.5, 3], 'avg': [5, 2], 'pes': [4.5,1.5]} ] """ # Determine undominated points (undominated_points_ind, undominated_points ) = self.utils.identify_undominated_points(REGION) # Determine pessimistic pareto front (pess_pareto, pess_indices_map) = self.utils.construct_pessimistic_pareto_front( undominated_points_ind, undominated_points, "CONSTRUCT") # Determine optimistic pareto front (opt_pareto, opt_indices_map) = self.utils.construct_optimistic_pareto_front( undominated_points_ind, undominated_points, "CONSTRUCT") # Determine pessimistic pareto volume pess_pareto_volume = self.utils.compute_pareto_volume(pess_pareto) # Determine optimistic pareto volume opt_pareto_volume = self.utils.compute_pareto_volume(opt_pareto) # Determine volume of the pareto front volume_of_pareto_front = opt_pareto_volume - pess_pareto_volume # Determine next configuration and objective (next_sample_index, next_sample, objective) = self.sample.determine_next_sample( pess_pareto, opt_pareto, pess_indices_map, opt_indices_map, pess_pareto_volume, opt_pareto_volume, REGION, self.E) # Perform measurement on next sample on the objective returned # Update init_X and init_Y if objective == "o1": cur_X1 = np.array(next_sample) cur_Y1 = np.array(self.rf1.predict([cur_X1])) self.O[next_sample_index]["o1"] = True self.measurement[next_sample_index]["o1"] = cur_Y1[0] np.vstack((init_X1, cur_X1)) np.vstack((init_Y1, cur_Y1)) if objective == "o2": cur_X2 = np.array(next_sample) cur_Y2 = np.array(self.rf2.predict([cur_X2])) self.O[next_sample_index]["o2"] = True self.measurement[next_sample_index]["o2"] = cur_Y2[0] np.vstack((init_X2, np.array(next_sample))) np.vstack((init_Y2, cur_Y2))
class Sample(object): """This class is used to determine next sample and objective """ def __init__(self): print("Initializing Sample Class") self.O1_IND = 1 self.O2_IND = 0 self.NUM_OBJ = 2 self.O1_COST = 1 self.O2_COST = 17.6 * self.O1_COST self.utils = Utils() def determine_next_sample(self, pess_pareto, opt_pareto, pess_indices_map, opt_indices_map, pess_pareto_volume, opt_pareto_volume, REGION, E): """@DETERMINE_NEXT_SAMPLE ------------------------------------------------------------------------ This function is used to determine next sample ------------------------------------------------------------------------ """ if pess_indices_map == opt_indices_map: indices_map = pess_indices_map pess_ind = [indices_map[i] for i in range(0, len(pess_pareto))] opt_ind = [indices_map[i] for i in range(0, len(opt_pareto))] pess_status = [[{ "pess": True, "opt": True }] if i in opt_ind else [{ "pess": True, "opt": False }] for i in pess_ind] opt_status = [[{ "pess": True, "opt": True }] if i in pess_ind else [{ "pess": False, "opt": True }] for i in opt_ind] #----------------------------------------------------------------------- # compute dv/c for each point in pessimistic pareto front #----------------------------------------------------------------------- dv_per_cost_pess = [{"o1": 0, "o2": 0} for i in pess_ind] for i in range(0, len(pess_pareto)): for j in range(0, self.NUM_OBJ): # Update pessimistic pareto front shring pess to avg if pess_status[i][0]["pess"] is True: cur_pess = pess_pareto[:] # replace pess with avg value across O1 cur_pess[i][j] = REGION[pess_ind[i]]["avg"][j] cur_pess_pareto = self.utils.construct_pessimistic_pareto_front( pess_ind, cur_pess, "UPDATE") cur_pess_volume = self.utils.compute_pareto_volume( cur_pess_pareto) # Update optimistic pareto front shring pess to avg if pess_status[i][0]["opt"] is True: cur_opt = opt_pareto[:] opt_i = opt_ind.index(pess_ind[i]) # replace opt with avg value across O1 cur_opt[opt_i][j] = REGION[opt_ind[i]]["avg"][j] cur_opt_pareto = self.utils.construct_optimistic_pareto_front( opt_ind, cur_opt, "UPDATE") cur_opt_volume = self.utils.compute_pareto_volume( cur_opt_pareto) # Shrinking of pessimistic pareto points do not change optimistic # pareto front if pess_status[i][0]["opt"] is False: cur_opt_volume = opt_pareto_volume dv = (opt_pareto_volume - pess_pareto_volume) - (cur_opt_volume - cur_pess_volume) if j == self.O1_IND: dv_per_cost_pess[i]["o1"] = dv / self.O1_COST if j == self.O2_IND: dv_per_cost_pess[i]["o2"] = dv / self.O2_COST #----------------------------------------------------------------------- # compute dv/c for each point in optimistic pareto front #----------------------------------------------------------------------- dv_per_cost_opt = [{"o1": 0, "o2": 0} for i in opt_ind] for i in range(0, len(opt_pareto)): for j in range(0, self.NUM_OBJ): # Update optimistic pareto front shring opt to avg. Similar points # both in pess and opt pareto fronts are already covered in # pessimistic pareto front update computation if (opt_status[i][0]["opt"] is True and opt_status[i][0]["opt"] is False): cur_opt = opt_pareto[:] # replace pess with avg value across O1 cur_opt[i][j] = REGION[opt_ind[i]]["avg"][j] cur_opt_pareto = self.utils.construct_optimistic_pareto_front( opt_ind, cur_opt, "UPDATE") cur_opt_volume = self.utils.compute_pareto_volume( cur_opt_pareto) # Shrinking of optimistic pareto points do not change pessimistic # pareto front cur_pess_volume = pess_pareto_volume # Compute dv per cost for each objective dv = (opt_pareto_volume - pess_pareto_volume) - (cur_opt_volume - cur_pess_volume) if j == self.O1_IND: dv_per_cost_pess[i]["o1"] = dv / self.O1_COST if j == self.O2_IND: dv_per_cost_pess[i]["o2"] = dv / self.O2_COST #----------------------------------------------------------------------- # Compute max dv per cost to determine the next sample and objective #----------------------------------------------------------------------- max_dv_per_cost = 0 objective = "o1" # Compute max dv per cost from pessimistic pareto points for i in range(0, len(dv_per_cost_pess)): if abs(dv_per_cost_pess[i]["o1"]) >= max_dv_per_cost: max_dv_per_cost_ind = i max_dv_per_cost = abs(dv_per_cost_pess[i]["o1"]) objective = "o1" if abs(dv_per_cost_pess[i]["o2"]) >= max_dv_per_cost: max_dv_per_cost_ind = i max_dv_per_cost = abs(dv_per_cost_pess[i]["o2"]) objective = "o2" cur_dv_per_cost_ind = indices_map[max_dv_per_cost_ind] # Compute max dv per cost from optimistic pareto points for i in range(0, len(dv_per_cost_opt)): if abs(dv_per_cost_opt[i]["o1"]) >= max_dv_per_cost: max_dv_per_cost_ind = i max_dv_per_cost = abs(dv_per_cost_pess[i]["o1"]) objective = "o1" if abs(dv_per_cost_opt[i]["o2"]) >= max_dv_per_cost: max_dv_per_cost_ind = i max_dv_per_cost = abs(dv_per_cost_pess[i]["o2"]) objective = "o2" # Compute next sample cur_dv_per_cost_ind = indices_map[max_dv_per_cost_ind] next_sample = E[cur_dv_per_cost_ind] return (cur_dv_per_cost_ind, next_sample, objective)