class Sampling(object): """This class is used to determine next sample and objective """ def __init__(self, o1_ind, o2_ind, o1_cost, o2_cost): print("[STATUS]: Initializing Sample Class") self.O1_IND = o1_ind self.O2_IND = o2_ind self.NUM_OBJ = 2 self.O1_COST = o1_cost self.O2_COST = o2_cost self.utils = Utils(o1_ind, o2_ind) 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_opt[i]["o1"] = dv / self.O1_COST if j == self.O2_IND: dv_per_cost_opt[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)
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, surrogate): print("Initializing FlexiBO class") self.df = data with open("config.yaml", "r") as fp: config = yaml.load(fp) cfg = ConfigSpaceReal("hardware", "os", config["config"]["network"]["net"]) (self.E, self.O, self.measurement) = cfg.set_design_space() self.network = config["config"]["network"]["net"] self.NUM_ITER = 200 self.NUM_OBJ = 2 self.O1_IND = config["config"]["index"]["O1"] self.O2_IND = config["config"]["index"]["O2"] self.m1 = config["config"]["objective"]["O1"] self.m2 = config["config"]["objective"]["O2"] self.O1_COST = config["config"]["evaluation_cost"]["O1"] self.O2_COST = config["config"]["evaluation_cost"]["O2"] self.sampling = Sampling(self.O1_IND, self.O2_IND, self.O1_COST, self.O2_COST) self.utils = Utils(self.O1_IND, self.O2_IND) self.surrogate = surrogate if self.surrogate == "GP": from src.surrogate_model import GPSurrogateModel self.SM = GPSurrogateModel() else: print("[ERROR]: Surrogate model not supported") (self.X, self.Y1, self.Y2) = self.prepare_training_data() self.perform_bo_loop() def prepare_training_data(self): """This function is used to prepare training data """ X = self.df[[ "num_cores", "core_freq", "gpu_freq", "emc_freq", "cache_pressure", "swappiness", "dirty_bg", "dirty_ratio", "entry_num_filters", "entry_filter_size", "middle_num_filters", "middle_filter_size", "exit_filter_size" ]].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), 20) 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[:] # bo loop for iteration in range(0, self.NUM_ITER): print("---------------------------------------Iteration: ", iteration) REGION = [{} for _ in U] if self.surrogate == "GP": # Fit a GP for each objective gpr1, gpr2 = self.SM.fit_gp() model_o1 = gpr1.fit(init_X1, init_Y1) model_o2 = gpr2.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: if self.surrogate == "GP": 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: if self.surrogate == "GP": 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 ] # 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.sampling.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": # Evaluate Objective O1 ConfigHardware(next_sample) ComputePerformance(fname_model) cur_X1 = np.array(next_sample) 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) ConfigNetwork(self.network, next_sample) ComputePerformance(fname_model) 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))