def get_offsprings(n, m, sig, lambd, sqrt_of_eig_vals, eig_vctrs, t, benchmarkfunction, experiment_data, cma_np_rnd_generator): offspring_population = np.zeros((lambd, n)) offspring_fitnesses = np.zeros(lambd) # decompose covariance matrix for sampling ("The CMA evolution strategy: a # tutorial, Hansen (2016), p. 28+29) # A = B*sqrt(M), with C = BMB^T (C=covariance matrix) A = np.matmul(eig_vctrs, sqrt_of_eig_vals) for k in range(lambd): z_k = cma_np_rnd_generator.normal(size=n) y_k = np.matmul(A, z_k) x_k = m + sig * y_k # x_k is equal to the following line but saves eigendecompositions: # m + sig * cma_np_rnd_generator.multivariate_normal(np.zeros(n), C) f_k = utils_dynopt.fitness(benchmarkfunction, x_k, t, experiment_data) offspring_population[k] = copy.copy(x_k) offspring_fitnesses[k] = f_k # if x_k is complex, f_k is unequal to fitness(offspring_population[k]) # (28.8.19) return offspring_population, offspring_fitnesses
def __init__(self, benchmarkfunction, dim, n_generations, experiment_data, predictor_name, pso_np_rnd_generator, pred_np_rnd_generator, c1, c2, c3, insert_pred_as_ind, adaptive_c3, n_particles, timesteps, n_neurons, epochs, batchsize, n_layers, apply_tl, n_tllayers, tl_model_path, tl_learn_rate, max_n_chperiod_reps): ''' Initialize a DynamicPSO object. @param benchmarkfunction: (string) @param dim: (int) dimensionality of objective function, i.e. number of features for each individual @param n_generations: (int) number of generations @param experiment_data: (dictionary) @param predictor_name: (string) @param pso_np_rnd_generator: numpy random generator for the PSO @param pred_np_rnd_generator: numpy random generator for the predictor @param c1: (float) influence of particle's best solution @param c2: (float) influence of swarm's best solution @param c3: (float) influence of prediction @param insert_pred_as_ind: (bool) True if the predicted optimum should be inserted into the swarm @param adaptive_c3: (bool) True if c3 is set with adaptive control @param n_particles: (int) swarm size @param time_steps: (int) number of time steps the predictions use for the prediction @param n_neurons: (int) number of neurons within the first layer of the RNN prediction model @param epochs: (int) number of epochs to train the RNN predicton model @param batch_size: (int) batch size for the RNN predictor ''' # --------------------------------------------------------------------- # for the problem # --------------------------------------------------------------------- self.benchmarkfunction = benchmarkfunction self.dim = dim self.n_generations = n_generations self.experiment_data = experiment_data self.predictor_name = predictor_name # --------------------------------------------------------------------- # for the predictor # --------------------------------------------------------------------- self.n_time_steps = timesteps self.n_neurons = n_neurons self.n_epochs = epochs self.batch_size = batchsize self.n_layers = n_layers self.apply_tl = apply_tl self.tl_model_path = tl_model_path self.n_tllayers = n_tllayers # --------------------------------------------------------------------- # for PSO (fixed values) # --------------------------------------------------------------------- self.pso_np_rnd_generator = pso_np_rnd_generator self.pred_np_rnd_generator = pred_np_rnd_generator self.c1 = c1 self.c2 = c2 self.c3 = c3 self.insert_pred_as_ind = insert_pred_as_ind self.adaptive_c3 = adaptive_c3 self.n_particles = n_particles # --------------------------------------------------------------------- # values that are not passed as parameters to the constructor # --------------------------------------------------------------------- self.init_c1 = self.c1 self.init_c2 = self.c2 self.init_c3 = self.c3 # TODO(exe) set following parameters as desired self.init_inertia = 0.7298 self.inertia = self.init_inertia self.vmax = 1000.0 # must not be too small self.inertia_adapt_factor = 0.5 # like tau for Rechenberg self.inertia_min = 0.1 self.inertia_max = 0.78540 # --------------------------------------------------------------------- # for PSO (variable values) # --------------------------------------------------------------------- # initialize population and compute fitness. # np.random.rand has values in [0, 1). Therefore multiply with 100 for # larger values (one row for each particle) self.particles = self.pso_np_rnd_generator.rand( self.n_particles, self.dim) * 100 # TODO(exe) self.v_vals = self.pso_np_rnd_generator.rand(self.n_particles, self.dim) # 1d numpy array self.fitness_vals = np.array([ utils_dynopt.fitness(self.benchmarkfunction, p, 0, self.experiment_data) for p in self.particles ]) # best history value (position) for each particle # (2d numpy array: 1 row for each particle) self.p_best_pos = copy.copy(self.particles) self.p_best_fit = copy.copy(self.fitness_vals) # best history value (position) of whole swarm (1d numpy array: 1 row) self.s_best_pos = copy.copy(self.particles[np.argmin( self.fitness_vals)]) self.s_best_fit = np.min(self.fitness_vals) # --------------------------------------------------------------------- # for PSO (prediction and evaluation) # --------------------------------------------------------------------- # number of change period detected by the PSO self.detected_n_changes = 0 # 1d numpy array containing for each generation the number of the # detected change period it belongs to self.detected_chgperiods_for_gens = [] # storage for best fitness evaluation of each generation self.best_found_pos_per_gen = np.zeros((self.n_generations, self.dim)) self.best_found_fit_per_gen = np.zeros(self.n_generations) # position & fitness of found optima (one for each change period) self.best_found_pos_per_chgperiod = [] self.best_found_fit_per_chgperiod = [] # position & fitness of predicted optima (one for each change period) self.pred_opt_pos_per_chgperiod = [] self.pred_opt_fit_per_chgperiod = []
def optimize(self): ''' @param mu: population size @param la: lambda (offspring population size) @param dim: dimensionality of objective function, i.e. number of features for each individual @param ro: number parents for recombination @param sel_strategie: selection strategy: 'plus' or 'comma' @param t_rechenberg: number of mutations after which sigma is adapted @param tau: # 0 < tau < 1, for Rechenberg @return: best fitness occured in any iteration ''' # --------------------------------------------------------------------- # local variables for predictor # --------------------------------------------------------------------- train_data = [] n_features = self.dim predictor = build_predictor(self.predictor_name, self.n_time_steps, n_features, self.batch_size, self.n_neurons) # denotes whether the predictor has been trained or not trained_first_time = False scaler = MyMinMaxScaler(feature_range=(-1, 1)) # --------------------------------------------------------------------- # local variables for PSO # --------------------------------------------------------------------- # number of particles that are better than p_best after particle update n_succ_particles = 0 # --------------------------------------------------------------------- for i in range(self.n_generations): print("generation: ", i) # adapt inertia weight self.adapt_inertia(n_succ_particles) # reset n_succ_particles after inertia adaptation n_succ_particles = 0 # test for environment change env_changed = environment_changed(i, self.particles, self.fitness_vals, self.benchmarkfunction, self.experiment_data, self.pso_np_rnd_generator) # iteration number in which the last change was detected last_it_with_chg = 0 if env_changed and i != 0: print("changed") # set c1, c2, c3 and inertia to their initial values self.reset_factors() # count change self.detected_n_changes += 1 last_it_with_chg = i # store best found solution during change period as training data for predictor # TODO(dev) works only for plus-selection (not for # comma-selection) self.best_found_pos_per_chgperiod.append( copy.copy(self.best_found_pos_per_gen[i - 1])) self.best_found_fit_per_chgperiod.append( copy.copy(self.best_found_fit_per_gen[i - 1])) overall_n_train_data = len(self.best_found_pos_per_chgperiod) # prevent training with too few train data if overall_n_train_data <= self.n_time_steps or overall_n_train_data < 50 or self.predictor_name == "no": my_pred_mode = "no" # train_data is empty list train_data = None prediction = None else: my_pred_mode = self.predictor_name # scale data scaler = scaler.fit(self.best_found_pos_per_chgperiod) transf_best_found_pos_per_chgperiod = scaler.transform( copy.copy(self.best_found_pos_per_chgperiod)) # choose training data if not trained_first_time: # first time, has not been trained before, therefore use all # found optimum positions trained_first_time = True train_data = transf_best_found_pos_per_chgperiod else: # append the last new train data (one) and in addition # n_time_steps already evaluated data in order to create a # whole time series of n_time_steps together with the new # data train_data = [] for step_idx in range(self.n_time_steps + 1, 0, -1): train_data.append( transf_best_found_pos_per_chgperiod[-step_idx]) train_data = np.array(train_data) # predict next optimum position prediction = predict_next_optimum_position( my_pred_mode, train_data, self.n_epochs, self.batch_size, self.n_time_steps, n_features, scaler, predictor, self.pred_np_rnd_generator) self.pred_opt_pos_per_chgperiod.append( copy.copy(prediction)) self.pred_opt_fit_per_chgperiod.append( utils_dynopt.fitness(self.benchmarkfunction, prediction, i, self.experiment_data)) # compute fitness again since fitness function changed self.fitness_vals = np.array([ utils_dynopt.fitness(self.benchmarkfunction, p, i, self.experiment_data) for p in self.particles ]) # reset p_best to current position self.p_best_pos = copy.copy(self.particles) self.p_best_fit = copy.copy(self.fitness_vals) # update s_best min_p_best_ind = np.argmin(self.p_best_fit) self.s_best_pos = copy.copy(self.p_best_pos[min_p_best_ind]) self.s_best_fit = self.p_best_fit[min_p_best_ind] # adapt population (replace worst by random individuals) if self.insert_pred_as_ind: pred_to_insert = copy.copy(prediction) else: pred_to_insert = None self.particles, self.fitness_vals = replace_worst_individuals( self.pso_np_rnd_generator, self.benchmarkfunction, i, self.particles, self.fitness_vals, self.n_particles, n_features, self.experiment_data, pred_to_insert) if prediction is None: self.p_pred_diff_vals = np.zeros( (self.n_particles, self.dim)) else: self.p_pred_diff_vals = prediction - self.particles else: self.p_pred_diff_vals = np.zeros((self.n_particles, self.dim)) # store which change period the current generation belongs to self.detected_chgperiods_for_gens.append(self.detected_n_changes) # random values # TODO new values in every iteration? r1_vals = self.pso_np_rnd_generator.rand(self.n_particles, self.dim) r2_vals = self.pso_np_rnd_generator.rand(self.n_particles, self.dim) r3_vals = self.pso_np_rnd_generator.rand(self.n_particles, self.dim) # update velocity self.s_best_diff_vals = self.s_best_pos - self.particles self.p_best_diff_vals = self.p_best_pos - self.particles # decrease importance of prediction with increasing #iterations try: c3 = self.c3 / (i - last_it_with_chg) except ZeroDivisionError: # if i == last_it_with_chg c3 = self.c3 c3 = self.c3 tmp1 = self.inertia * self.v_vals tmp2 = self.c1 * r1_vals * self.p_best_diff_vals tmp3 = self.c2 * r2_vals * self.s_best_diff_vals tmp4 = c3 * r3_vals * self.p_pred_diff_vals self.v_vals = tmp1 + tmp2 + tmp3 + tmp4 # velocity should be <= vmax too_large = np.abs(self.v_vals) > self.vmax self.v_vals[too_large] = (np.sign(self.v_vals) * self.vmax)[too_large] # update particles self.particles = self.particles + self.v_vals # compute fitness self.fitness_vals = np.array([ utils_dynopt.fitness(self.benchmarkfunction, p, i, self.experiment_data) for p in self.particles ]) # update p_best particle_better = self.fitness_vals < self.p_best_fit n_succ_particles = np.sum(particle_better) self.p_best_pos[particle_better] = copy.copy( self.particles[particle_better]) self.p_best_fit[particle_better] = self.fitness_vals[ particle_better] # update s_best self.s_best_pos = copy.copy(self.p_best_pos[np.argmin( self.p_best_fit)]) self.s_best_fit = np.min(self.p_best_fit) # determine best particles/fitness (for evaluation/statistic) self.best_found_fit_per_gen[i] = copy.copy(self.s_best_fit) self.best_found_pos_per_gen[i] = copy.copy(self.s_best_pos) # adapt c3 if self.adaptive_c3: self.adapt_c3() # store things last time self.best_found_pos_per_chgperiod.append( copy.copy(self.best_found_pos_per_gen[self.n_generations - 1])) self.best_found_fit_per_chgperiod.append( copy.copy(self.best_found_fit_per_gen[self.n_generations - 1])) # conversion self.best_found_pos_per_chgperiod = np.array( self.best_found_pos_per_chgperiod) self.pred_opt_pos_per_chgperiod = np.array( self.pred_opt_pos_per_chgperiod)
def prepare_data_train_and_predict( sess, gen_idx, n_features, predictors, experiment_data, n_epochs, batch_size, return_seq, shuffle_train_data, n_new_train_data, best_found_pos_per_chgperiod, train_interval, predict_diffs, n_time_steps, n_required_train_data, predictor_name, add_noisy_train_data, n_noisy_series, stddev_among_runs_per_chgp, test_mc_runs, benchmarkfunction, use_uncs, pred_unc_per_chgperiod, aleat_unc_per_chgperiod, pred_opt_pos_per_chgperiod, pred_opt_fit_per_chgperiod, train_error_per_chgperiod, train_error_for_epochs_per_chgperiod, glob_opt, trueprednoise, pred_np_rnd_generator): ''' TODO use this function in dynpso ''' n_past_chgps = len(best_found_pos_per_chgperiod) # number of train data that can be produced from the last chg. periods overall_n_train_data = calculate_n_train_samples(n_past_chgps, predict_diffs, n_time_steps) # prevent training with too few train data if (overall_n_train_data < n_required_train_data or predictor_name == "no"): my_pred_mode = "no" train_data = None prediction = None else: my_pred_mode = predictor_name # number of required change periods (to construct training data) n_required_chgps = calculate_n_required_chgps_from_n_train_samples( n_required_train_data, predict_diffs, n_time_steps) best_found_vals_per_chgperiod = best_found_pos_per_chgperiod[ -n_required_chgps:] # transform absolute values to differences if predict_diffs: best_found_vals_per_chgperiod = np.subtract( best_found_vals_per_chgperiod[1:], best_found_vals_per_chgperiod[:-1]) # scale data (the data are re-scaled directly after the # prediction in this iteration) scaler = fit_scaler(best_found_vals_per_chgperiod) train_data = scaler.transform(copy.copy(best_found_vals_per_chgperiod)) # add noisy training data in order to make network more robust and # increase the number of training data if add_noisy_train_data: # 3d array [n_series, n_chgperiods, dims] noisy_series = get_noisy_time_series( np.array(best_found_pos_per_chgperiod), n_noisy_series, stddev_among_runs_per_chgp, pred_np_rnd_generator) if predict_diffs: noisy_series = np.array([ np.subtract(noisy_series[i, 1:], noisy_series[i, :-1]) for i in range(len(noisy_series)) ]) # scale data noisy_series = np.array([ scaler.transform(copy.copy(noisy_series[i])) for i in range(len(noisy_series)) ]) else: noisy_series = None # train data train_data = np.array(train_data) # train the model only when train_interval new data are available do_training = n_new_train_data >= train_interval if do_training: n_new_train_data = 0 # predict next optimum position or difference (and re-scale value) prdctns = [] prdctns_fit = [] pred_unc_per_predictor = [] avg_al_unc_per_predictor = [] train_error_per_predictor = [] train_err_per_epoch_per_predictor = [] prdct_nms = [] for prdctr_name in predictors.keys(): # make prediction with each single predictor (prdctn, train_error, train_err_per_epoch, pred_unc, avg_al_unc, ar_predictor) = predict_next_optimum_position( prdctr_name, sess, train_data, noisy_series, n_epochs, batch_size, n_time_steps, n_features, scaler, predictors[prdctr_name], return_seq, shuffle_train_data, do_training, best_found_pos_per_chgperiod, predict_diffs, test_mc_runs, n_new_train_data, glob_opt, trueprednoise, pred_np_rnd_generator) prdctns.append(prdctn) prdctns_fit.append( utils_dynopt.fitness(benchmarkfunction, prdctn, gen_idx, experiment_data)) pred_unc_per_predictor.append(pred_unc) avg_al_unc_per_predictor.append(avg_al_unc) train_error_per_predictor.append(train_error) train_err_per_epoch_per_predictor.append(train_err_per_epoch) prdct_nms.append(prdctr_name) if prdctr_name == "autoregressive": predictors[prdctr_name] = ar_predictor # index of best prediction min_idx = np.argmin(prdctns_fit) prediction = prdctns[min_idx] prediction_fit = prdctns_fit[min_idx] pred_unc = pred_unc_per_predictor[min_idx] avg_al_unc = avg_al_unc_per_predictor[min_idx] train_error = train_error_per_predictor[min_idx] train_err_per_epoch = train_err_per_epoch_per_predictor[min_idx] # store results pred_opt_pos_per_chgperiod.append(copy.copy(prediction)) pred_opt_fit_per_chgperiod.append(prediction_fit) if pred_unc is not None and use_uncs: pred_unc_per_chgperiod.append(copy.copy(pred_unc)) if avg_al_unc is not None and use_uncs: aleat_unc_per_chgperiod.append(copy.copy(avg_al_unc)) train_error_per_chgperiod.append(train_error) train_error_for_epochs_per_chgperiod.append(train_err_per_epoch) return my_pred_mode, predictors, n_new_train_data
def __init__(self, benchmarkfunction, dim, lenchgperiod, n_generations, experiment_data, predictor_name, trueprednoise, lbound, ubound, cma_np_rnd_generator, pred_np_rnd_generator, timesteps, n_neurons, epochs, batchsize, n_layers, train_interval, n_required_train_data, use_uncs, train_mc_runs, test_mc_runs, train_dropout, test_dropout, kernel_size, n_kernels, lr, cma_variant, pred_variant): ''' Constructor ''' # TODOs: # - alle "Speichervariablen" vom EA nutzen, damit Ausgabedateien identisch sind # --------------------------------------------------------------------- # for the problem # --------------------------------------------------------------------- self.benchmarkfunction = benchmarkfunction self.dim = dim self.lenchgperiod = lenchgperiod self.n_generations = n_generations # TODO rename self.experiment_data = experiment_data self.predictor_name = predictor_name self.trueprednoise = trueprednoise self.lbound = lbound # 100 # assumed that the input data follow this assumption self.ubound = ubound # 200 # TODO(exe) , TODO insert into dynPSO # --------------------------------------------------------------------- # for the predictor # --------------------------------------------------------------------- self.n_time_steps = timesteps self.n_neurons = n_neurons self.n_epochs = epochs self.batch_size = batchsize self.n_layers = n_layers self.train_interval = train_interval # predictive uncertainty self.train_mc_runs = train_mc_runs self.test_mc_runs = test_mc_runs self.train_dropout = train_dropout self.test_dropout = test_dropout self.use_uncs = use_uncs # True if uncertainties should be trained, predicted and used # TCN self.kernel_size = kernel_size self.n_kernels = n_kernels self.lr = lr # training/testing specifications # number train data with that the network at least is trained self.n_required_train_data = max(n_required_train_data, self.n_time_steps) self.predict_diffs = True # predict position differences, TODO insert into PSO self.return_seq = False # return values for all time steps not only the last one # True -> train data are shuffled before training and between epochs self.shuffle_train_data = True # TODO move into script? # --------------------------------------------------------------------- # for EA/CMA-ES (fixed values) # --------------------------------------------------------------------- self.cma_np_rnd_generator = cma_np_rnd_generator self.pred_np_rnd_generator = pred_np_rnd_generator # ------------------------------------------------------------------------- # fixed parameters # ------------------------------------------------------------------------- self.lambd = 4 + floor(3 * log(self.dim)) # offsprings self.mu = floor(self.lambd / 2) # parents # weights (vector of size ń) w_divisor = np.sum([(log(self.mu + 0.5) - log(j)) for j in range(1, self.mu + 1)]) self.w = np.array([((log(self.mu + 0.5) - log(i)) / w_divisor) for i in range(1, self.mu + 1)]) # other self.mu_w = 1 / np.sum(np.square(self.w)) self.c_sig = (self.mu_w + 2) / (self.dim + self.mu_w + 3) self.d_sig = 1 + self.c_sig + 2 * \ max(0, sqrt((self.mu_w - 1) / (self.dim + 1)) - 1) self.c_c = 4 / (self.dim + 4) self.c_1 = (2 * min(1, self.lambd / 6)) / \ ((self.dim + 1.3)**2 + self.mu_w) self.c_mu = (2 * (self.mu_w - 2 + 1 / self.mu_w)) / \ ((self.dim + 2)**2 + self.mu_w) self.E = sqrt(self.dim) * \ (1 - 1 / (4 * self.dim) + 1 / (21 * self.dim**2)) self.c_o = self.c_c self.c_o1 = self.c_1 # ------------------------------------------------------------------------- # initialization # ------------------------------------------------------------------------- self.m = self.cma_np_rnd_generator.randint(self.lbound, self.ubound, self.dim) self.sig = cma_np_rnd_generator.rand() self.p_sig = np.zeros(self.dim) self.p_c = np.zeros(self.dim) self.C = np.identity(self.dim) self.p_sig_pred = np.zeros(self.dim) # --------------------------------------------------------------------- # options # --------------------------------------------------------------------- self.cma_variant = cma_variant self.pred_variant = pred_variant # --------------------------------------------------------------------- # values that are not passed as parameters to the constructor # --------------------------------------------------------------------- self.init_sigma = 1 # --------------------------------------------------------------------- # for EA (variable values) # --------------------------------------------------------------------- # initialize population (mu candidates) and compute fitness. self.population = self.cma_np_rnd_generator.uniform( self.lbound, self.ubound, (self.mu, self.dim)) # 2d numpy array (for each individual one row) self.population_fitness = np.array([ utils_dynopt.fitness(self.benchmarkfunction, individual, 0, self.experiment_data) for individual in self.population ]).reshape(-1, 1) # --------------------------------------------------------------------- # for EA (prediction and evaluation) # --------------------------------------------------------------------- # number of change periods (new training data) since last training self.n_new_train_data = 0 # number of changes detected by the EA self.detected_n_changes = 0 # for each detected change the corresponding generation numbers self.detected_chgperiods_for_gens = [] # best found individual for each generation (2d numpy array) self.best_found_pos_per_gen = np.zeros((self.n_generations, self.dim)) # fitness of best found individual for each generation (1d numpy array) self.best_found_fit_per_gen = np.zeros(self.n_generations) # position of found optima (one for each change period) self.best_found_pos_per_chgperiod = [] # fitness of found optima (one for each change period) self.best_found_fit_per_chgperiod = [] # position, fitness & epistemic uncertainty of predicted optima (one for # each change period) self.pred_opt_pos_per_chgperiod = [] self.pred_opt_fit_per_chgperiod = [] self.pred_unc_per_chgperiod = [] # predictive variance self.aleat_unc_per_chgperiod = [] # average aleatoric uncertainty # training error per chgperiod (if prediction was done) self.train_error_per_chgperiod = [] # training error per epoch for each chgperiod (if prediction was done) self.train_error_for_epochs_per_chgperiod = [] # --------------------------------------------------------------------- # CMA-ES variables # --------------------------------------------------------------------- #self.angle_per_gen = [] #self.sig_per_gen = [] #self.p_sig_per_gen = [] #self.h_sig_per_gen = [] #self.p_c_per_gen = [] #self.p_sig_pred_per_gen = [] #self.m_per_gen = [] # global optimum self.glob_opt_per_gen = [] # sigma and mean self.sig_per_gen = [] self.m_per_gen = [] self.p_sig_pred_per_chgp = [] # --------------------------------------------------------------------- # for EA (evaluation of variance) (repetitions of change periods) # not used for CMA-ES, only for similar output files like EA # --------------------------------------------------------------------- # add noisy data (noise equals standard deviation among change period # runs TODO could be replaced by "max_n_chgperiod_reps > 1" self.add_noisy_train_data = False # False since unused self.n_noisy_series = 20 # number repetitions of the single change periods (at least 1 -> 1 run) # TODO insert into PSO self.max_n_chgperiod_reps = 1 # arbitrary value since unused # population for last generation of change period (for each run) # used for determining the EAs variance for change periods # 4d list [runs, chgperiods, parents, dims] # TODO insert into PSO self.final_pop_per_run_per_chgperiod = [ [] for _ in range(self.max_n_chgperiod_reps) ] # fitness of final population per run of each chgperiod # 3d list [runs, chgperiods, parents] # TODO insert into PSO self.final_pop_fitness_per_run_per_changeperiod = [ [] for _ in range(self.max_n_chgperiod_reps) ] # best found solution for each run of all change periods # format [runs, chgperiods, dims] self.best_found_pos_per_run_per_chgp = [ [] for _ in range(self.max_n_chgperiod_reps) ] # standard deviation and mean of the position of the best found solution # (computed over the change period runs) self.stddev_among_runs_per_chgp = [ [] for _ in range(self.max_n_chgperiod_reps) ] self.mean_among_runs_per_chgp = [ [] for _ in range(self.max_n_chgperiod_reps) ] # --------------------------------------------------------------------- assert (pred_variant in ["simplest", "a", "b", "c", "d", "g", "p", "pwm"] and cma_variant == "predcma_external") or \ ((pred_variant in ["branke", "f"] or pred_variant.startswith("h")) and cma_variant == "predcma_internal") or \ pred_variant == "None" and cma_variant in ["static", "resetcma"]
def repeat_selected_generations(self, generation_idcs, run_idcs, original_pop, original_pops_fit): ''' @param generation_idcs: contains indices of generations that are repeated @param run_idcs: contains indices of runs that are repeated (begin with 1 since one run already is run before) @param original_pop: population ''' # --------------------------------------------------------------------- # local variables for EA # --------------------------------------------------------------------- # number of successful mutations during t_rechenberg generations t_s = 0 # overall number of mutations t_all = 0 # --------------------------------------------------------------------- # store old values of class variables old_pop = copy.deepcopy(self.population) old_pop_fit = copy.deepcopy(self.population_fitness) # --------------------------------------------------------------------- #print("repeat chgperiod", flush=True) for r in run_idcs: #print(" repetition: ", r, flush=True) # set new values to class variables self.population = copy.deepcopy(original_pop) self.population_fitness = copy.deepcopy(original_pops_fit) for i in generation_idcs: # create la offsprings offspring_population = np.zeros((self.la, self.dim)) offspring_pop_fitness = np.zeros((self.la, 1)) for j in range(self.la): # adapt sigma after t_rechenberg mutations if t_all % self.t_rechenberg == 0 and t_all != 0: adapt_sigma(self.sigma, self.t_rechenberg, self.tau, t_s) # reset counters t_all = 0 t_s = 0 # recombination offspring = self.recombinate() # mutation mutated_offspring = self.mutate(offspring) t_all += 1 # evaluate offspring offspring_fitness = utils_dynopt.fitness( self.benchmarkfunction, mutated_offspring, i, self.experiment_data) # add offspring to offspring population offspring_population[j] = copy.copy(mutated_offspring) offspring_pop_fitness[j][0] = offspring_fitness # mutation successful? if offspring_fitness < utils_dynopt.fitness( self.benchmarkfunction, offspring, i, self.experiment_data): t_s += 1 # select new population self.select(offspring_population, offspring_pop_fitness) # save final population of this run and its fitness values self.final_pop_per_run_per_chgperiod[r].append( copy.deepcopy(self.population)) self.final_pop_fitness_per_run_per_changeperiod[r].append( copy.deepcopy(self.population_fitness)) # determine best found position (with minimum fitness) min_fitness_index = np.argmin(self.population_fitness) self.best_found_pos_per_run_per_chgp[r].append( copy.deepcopy(self.population[min_fitness_index])) # --------------------------------------------------------------------- # compute standard deviation of best found position among runs # --------------------------------------------------------------------- # [chgperiods, dims] self.stddev_among_runs_per_chgp = np.std( self.best_found_pos_per_run_per_chgp, axis=0) self.mean_among_runs_per_chgp = np.average( self.best_found_pos_per_run_per_chgp, axis=0) # --------------------------------------------------------------------- # restore old values # --------------------------------------------------------------------- self.population = old_pop self.population_fitness = old_pop_fit
def optimize(self): # --------------------------------------------------------------------- # local variables for predictor # --------------------------------------------------------------------- predictors = build_all_predictors( self.predictor_name, self.n_time_steps, self.dim, self.batch_size, self.n_neurons, self.return_seq, self.apply_tl, self.n_layers, self.n_epochs, self.tl_rnn_type, self.n_tllayers, self.with_dense_first, self.tl_learn_rate, self.use_uncs, self.train_mc_runs, self.train_dropout, self.test_dropout, self.kernel_size, self.n_kernels, self.lr) sess = None # necessary since is assigned anew after each training if self.predictor_name in [ "rnn", "tfrnn", "tftlrnn", "tftlrnndense", "tcn", "hybrid-autoregressive-rnn" ]: import tensorflow as tf # if transfer learning then load weights if self.apply_tl: # instantiate saver to restore pre-trained weights/biases tl_variables, _, _, _, _, _ = get_variables_and_names( self.n_tllayers) saver = tf.train.Saver(tl_variables) # start session config = tf.ConfigProto() config.gpu_options.allow_growth = True sess = tf.Session(config=config) # initialize empty model (otherwise exception) sess.run(tf.global_variables_initializer()) sess.run(tf.local_variables_initializer()) if self.apply_tl: # overwrite initial values with pre-trained weights/biases saver.restore(sess, self.tl_model_path) # --------------------------------------------------------------------- # local variables for EA # --------------------------------------------------------------------- # number of successful mutations during t_rechenberg generations t_s = 0 # overall number of mutations t_all = 0 # --------------------------------------------------------------------- # generations for that the repetitions are executed (is initialized # first time: all generations until a change is detected are appended) gens_for_rep = [] # --------------------------------------------------------------------- start_pop_for_curr_chgperiod = self.population start_pops_fit_for_curr_chgperiod = self.population_fitness for i in range(self.n_generations): glob_opt = self.experiment_data['global_opt_pos_per_gen'][i] #print("generation , ", i, " glob opt: ", glob_opt) # store generations that have to be repeated (to compute the # variance of the ES) gens_for_rep.append(i) # test for environment change env_changed = environment_changed(i, self.population, self.population_fitness, self.benchmarkfunction, self.experiment_data, self.ea_np_rnd_generator) # test for environment change (but not in first generation) if env_changed and i != 0: # ------------------------------------------- # for repetitions of chgperiods: # save population for first run of current chgperiod self.final_pop_per_run_per_chgperiod[0].append( copy.deepcopy(self.population)) self.final_pop_fitness_per_run_per_changeperiod[0].append( copy.deepcopy(self.population_fitness)) self.best_found_pos_per_run_per_chgp[0].append( copy.deepcopy(self.best_found_pos_per_gen[i - 1])) # ------------------------------------------- # only for experiments with repetitions of change periods if self.max_n_chgperiod_reps > 1: run_indices = range(1, self.max_n_chgperiod_reps) self.repeat_selected_generations( gens_for_rep, run_indices, start_pop_for_curr_chgperiod, start_pops_fit_for_curr_chgperiod) gens_for_rep = [] # in the following the population of the first run is used (for prediction,...) # ------------------------------------------- # reset sigma to initial value self.reset_parameters() # count change self.detected_n_changes += 1 # count new train data self.n_new_train_data += 1 print("(chg/gen)-(" + str(self.detected_n_changes) + "/" + str(i) + ") ", end="", flush=True) # store best found solution during change period as training data for predictor # TODO(dev) works only for plus-selection (not for # comma-selection) self.best_found_pos_per_chgperiod.append( copy.copy(self.best_found_pos_per_gen[i - 1])) self.best_found_fit_per_chgperiod.append( copy.copy(self.best_found_fit_per_gen[i - 1])) # prepare data and predict optimum (my_pred_mode, updated_predictors, self.n_new_train_data) = prepare_data_train_and_predict( sess, i, self.dim, predictors, self.experiment_data, self.n_epochs, self.batch_size, self.return_seq, self.shuffle_train_data, self.n_new_train_data, self.best_found_pos_per_chgperiod, self.train_interval, self.predict_diffs, self.n_time_steps, self.n_required_train_data, self.predictor_name, self.add_noisy_train_data, self.n_noisy_series, self.stddev_among_runs_per_chgp, self.test_mc_runs, self.benchmarkfunction, self.use_uncs, self.pred_unc_per_chgperiod, self.aleat_unc_per_chgperiod, self.pred_opt_pos_per_chgperiod, self.pred_opt_fit_per_chgperiod, self.train_error_per_chgperiod, self.train_error_for_epochs_per_chgperiod, glob_opt, self.trueprednoise, self.pred_np_rnd_generator) predictors = updated_predictors # adapt population to environment change self.adapt_population(i, my_pred_mode) # store population start_pop_for_curr_chgperiod = copy.deepcopy(self.population) start_pops_fit_for_curr_chgperiod = copy.deepcopy( self.population_fitness) self.detected_chgperiods_for_gens.append(self.detected_n_changes) # save start population of this generation self.population_of_last_gen = copy.copy(self.population) # create la offsprings offspring_population = np.zeros((self.la, self.dim)) offspring_pop_fitness = np.zeros((self.la, 1)) for j in range(self.la): # adapt sigma after t_rechenberg mutations if t_all % self.t_rechenberg == 0 and t_all != 0: adapt_sigma(self.sigma, self.t_rechenberg, self.tau, t_s) # reset counters t_all = 0 t_s = 0 # recombination offspring = self.recombinate() # mutation mutated_offspring = self.mutate(offspring) t_all += 1 # evaluate offspring offspring_fitness = utils_dynopt.fitness( self.benchmarkfunction, mutated_offspring, i, self.experiment_data) # add offspring to offspring population offspring_population[j] = copy.copy(mutated_offspring) offspring_pop_fitness[j][0] = offspring_fitness # mutation successful? if offspring_fitness < utils_dynopt.fitness( self.benchmarkfunction, offspring, i, self.experiment_data): t_s += 1 # select new population self.select(offspring_population, offspring_pop_fitness) min_fitness_index = np.argmin(self.population_fitness) self.best_found_fit_per_gen[i] = copy.copy( self.population_fitness[min_fitness_index]) self.best_found_pos_per_gen[i] = copy.copy( self.population[min_fitness_index]) # print("best: ", self.population_fitness[min_fitness_index], # "[", self.population[min_fitness_index], "]") if self.predictor_name in [ "tfrnn", "tftlrnn", "tftlrnndense", "tcn", "rnn", "hybrid-autoregressive-rnn" ]: sess.close() tf.reset_default_graph() # save results for last change period self.best_found_pos_per_chgperiod.append( copy.copy(self.best_found_pos_per_gen[i - 1])) self.best_found_fit_per_chgperiod.append( copy.copy(self.best_found_fit_per_gen[i - 1]))
def adapt_population(self, curr_gen, my_pred_mode): ''' Re-initialize (parts of) the population: insert immigrants that are generated by means of different re-initialization strategies. Explanations for re-initialization strategies (nRND, nVAR, nPRE, pKAL, pUNC, pDEV, pRND) can be found in the ICANN 2019 paper. ''' mean = 0.0 n_immigrants = self.mu assert n_immigrants == self.mu random_immigrants = self.ea_np_rnd_generator.uniform( self.lbound, self.ubound, (n_immigrants, self.dim)) if my_pred_mode == "no" or n_immigrants == 0: if self.reinitialization_mode == "no-RND" or self.predictor_name != "no": # completely random if no prediction is applied or the predictor # is only applied later when enough training data is available immigrants = random_immigrants elif self.reinitialization_mode == "no-VAR": # add to current individuals a noise with standard # deviation (x_t - x_t-1) sigma_per_ind, _ = self.get_delta() immigrants = np.array([ gaussian_mutation(x, mean, float(s), self.pred_np_rnd_generator) for x, s in zip(self.population, sigma_per_ind) ]) elif self.reinitialization_mode == "no-PRE": sigma_per_ind, parent_population = self.get_delta() predicted_pop = self.population + \ (self.population - parent_population) immigrants = np.array([ gaussian_mutation(x, mean, float(s), self.pred_np_rnd_generator) for x, s in zip(predicted_pop, sigma_per_ind) ]) else: warnings.warn("unknown reinitialization mode: " + self.reinitialization_mode) elif my_pred_mode in [ "rnn", "autoregressive", "tfrnn", "tftlrnn", "tftlrnndense", "tcn", "kalman", "truepred", "hybrid-autoregressive-rnn" ]: # last predicted optimum pred_optimum_position = self.pred_opt_pos_per_chgperiod[-1] # insert predicted optimum into immigrants immigrants = np.array([pred_optimum_position]) n_remaining_immigrants = n_immigrants - len(immigrants) # initialize remaining immigrants # a) within the neighborhood of the predicted optimum and # b) completely randomly # ratio of a) and b): 2/3, 1/3 if n_remaining_immigrants > 1: if self.reinitialization_mode == "pred-KAL": self.sigma_factors = [1.0] # a) # immigrants randomly in the area around the optimum (in case # of TCN the area is bound to the predictive variance). There # are 4 different realizations of this type. two_third = math.ceil((n_remaining_immigrants / 3) * 2) n_immigrants_per_noise = two_third // len(self.sigma_factors) for z in self.sigma_factors: noisy_optimum_positions = self.compute_noisy_opt_positions( z, pred_optimum_position, n_immigrants_per_noise) if len(noisy_optimum_positions) != 0: immigrants = np.concatenate( (immigrants, noisy_optimum_positions)) # b) TODO only use respective re-initialization strategy # initialize remaining immigrants completely randomly n_remaining_immigrants = n_immigrants - len(immigrants) immigrants = np.concatenate( (immigrants, random_immigrants[:n_remaining_immigrants])) else: # take one of the random immigrants immigrants = np.concatenate((immigrants, random_immigrants[0])) else: msg = "unknown prediction mode " + my_pred_mode warnings.warn(msg) assert len( immigrants) == n_immigrants, "false number of immigrants: " + str( len(immigrants)) # build new population self.population = np.concatenate((self.population, immigrants)) # compute fitness of new population self.population_fitness = np.array([ utils_dynopt.fitness(self.benchmarkfunction, individual, curr_gen, self.experiment_data) for individual in self.population ]).reshape(-1, 1)
def __init__(self, benchmarkfunction, dim, n_generations, experiment_data, predictor_name, trueprednoise, lbound, ubound, ea_np_rnd_generator, pred_np_rnd_generator, mu, la, ro, mean, sigma, trechenberg, tau, reinitialization_mode, sigma_factors, timesteps, n_neurons, epochs, batchsize, n_layers, apply_tl, n_tllayers, tl_model_path, tl_learn_rate, max_n_chperiod_reps, add_noisy_train_data, train_interval, n_required_train_data, use_uncs, train_mc_runs, test_mc_runs, train_dropout, test_dropout, kernel_size, n_kernels, lr): ''' Initialize a DynamicEA object. @param benchmarkfunction: (string) @param dim: (int) dimensionality of objective function, i.e. number of features for each individual @param n_generations: (int) number of generations @param experiment_data: (dictionary) @param predictor_name: (string) @param lbound, ubound: (floats) lower/upper bound of solution space; must fit the data in experiment_data! @param ea_np_rnd_generator: numpy random generator for the EA @param pred_np_rnd_generator: numpy random generator for the predictor @param mu: (int) population size @param la: (int) lambda, number of offspring individuals @param ro: (int) number parents for recombination @param mean: (float) mean for the Gaussian mutation of the EA @param sigma: (float) mutation strength for EA @param trechenberg: number of mutations after which sigma is adapted @param tau: 0 < tau < 1, factor to adapt sigma (for Rechenberg 1/5 rule) @param timesteps: (int) number of time steps the predictions use for the prediction @param n_neurons: (int) number of neurons within the first layer of the RNN prediction model @param epochs: (int) number of epochs to train the RNN predicton model @param batch_size: (int) batch size for the RNN predictor @param n_layers (int): overall number of RNN layers (incl. pre-trained ones), only for "tfrnn", "tftlrnn", or "tftlrnndense" @param apply_tl: (bool) True if transfer learning should be applied @param n_tllayers (int) number layers in pre-trained RNN only for "tfrnn", "tftlrnn", or "tftlrnndense" @param tl_model_path: (string) path to pre-trained RNN @param tl_learn_rate (float): learning rate for pre-trained layers @param max_n_chperiod_reps: (int): how often each change period should be repeated (used for evaluation of variance of EA) @param add_noisy_train_data (bool): True if the variance over the repeated change periods should be used as noise to disturbe the time series the prediction models learn from @param train_interval: (int) number of change periods that must have passed before predictor is trained anew @param n_required_train_data: (int) number of training data that is used for training @param use_uncs: (True) if predictive uncertainty should be estimated; only possible for predictors "kalman", "tcn" and "truepred" @param train_mc_runs: (int) number of Monte Carlo runs during training (when predictive uncertainty is estimated) @param test_mc_runs: (int) number of Monte Carlo runs during prediciton @param train_dropout, test_dropout (float): dropout for training/test @param kernel_size (int): filter size for "tcn" @param n_kernels: (int) number filters for "tcn" @param lr: (float) learning rate for "tcn" ''' # --------------------------------------------------------------------- # for the problem # --------------------------------------------------------------------- self.benchmarkfunction = benchmarkfunction self.dim = dim self.n_generations = n_generations self.experiment_data = experiment_data self.predictor_name = predictor_name self.trueprednoise = trueprednoise # TODO unused so far self.lbound = lbound # 100 # assumed that the input data follow this assumption self.ubound = ubound # 200 # TODO(exe) , TODO insert into dynPSO # --------------------------------------------------------------------- # for the predictor # --------------------------------------------------------------------- self.n_time_steps = timesteps self.n_neurons = n_neurons self.n_epochs = epochs self.batch_size = batchsize self.n_layers = n_layers self.train_interval = train_interval # predictive uncertainty self.train_mc_runs = train_mc_runs self.test_mc_runs = test_mc_runs self.train_dropout = train_dropout self.test_dropout = test_dropout self.use_uncs = use_uncs # True if uncertainties should be trained, predicted and used # TCN self.kernel_size = kernel_size self.n_kernels = n_kernels self.lr = lr # transfer learning self.apply_tl = apply_tl # True if pre-trained model should be used self.tl_model_path = tl_model_path # path to the trained tl model self.tl_rnn_type = "RNN" self.n_tllayers = n_tllayers self.with_dense_first = None self.tl_learn_rate = tl_learn_rate # training/testing specifications # number train data with that the network at least is trained self.n_required_train_data = max(n_required_train_data, self.n_time_steps) self.predict_diffs = True # predict position differences, TODO insert into PSO self.return_seq = False # return values for all time steps not only the last one # True -> train data are shuffled before training and between epochs self.shuffle_train_data = True # TODO move into script? # --------------------------------------------------------------------- # for EA (fixed values) # --------------------------------------------------------------------- self.ea_np_rnd_generator = ea_np_rnd_generator self.pred_np_rnd_generator = pred_np_rnd_generator self.mu = mu self.la = la self.ro = ro self.mean = mean self.sigma = sigma self.t_rechenberg = trechenberg self.tau = tau self.reinitialization_mode = reinitialization_mode self.sigma_factors = sigma_factors # --------------------------------------------------------------------- # values that are not passed as parameters to the constructor # --------------------------------------------------------------------- self.init_sigma = self.sigma # --------------------------------------------------------------------- # for EA (variable values) # --------------------------------------------------------------------- # initialize population (mu candidates) and compute fitness. self.population = self.ea_np_rnd_generator.uniform( self.lbound, self.ubound, (self.mu, self.dim)) # 2d numpy array (for each individual one row) self.population_fitness = np.array([ utils_dynopt.fitness(self.benchmarkfunction, individual, 0, self.experiment_data) for individual in self.population ]).reshape(-1, 1) # --------------------------------------------------------------------- # for EA (prediction and evaluation) # --------------------------------------------------------------------- # number of change periods (new training data) since last training self.n_new_train_data = 0 # number of changes detected by the EA self.detected_n_changes = 0 # for each detected change the corresponding generation numbers self.detected_chgperiods_for_gens = [] # best found individual for each generation (2d numpy array) self.best_found_pos_per_gen = np.zeros((self.n_generations, self.dim)) # fitness of best found individual for each generation (1d numpy array) self.best_found_fit_per_gen = np.zeros(self.n_generations) # position of found optima (one for each change period) self.best_found_pos_per_chgperiod = [] # fitness of found optima (one for each change period) self.best_found_fit_per_chgperiod = [] # position, fitness & epistemic uncertainty of predicted optima (one for # each change period) self.pred_opt_pos_per_chgperiod = [] self.pred_opt_fit_per_chgperiod = [] self.pred_unc_per_chgperiod = [] # predictive variance self.aleat_unc_per_chgperiod = [] # average aleatoric uncertainty # training error per chgperiod (if prediction was done) self.train_error_per_chgperiod = [] # training error per epoch for each chgperiod (if prediction was done) self.train_error_for_epochs_per_chgperiod = [] # stores the population of the (beginning of the) last generation self.population_of_last_gen = None # --------------------------------------------------------------------- # for EA (evaluation of variance) (repetitions of change periods) # --------------------------------------------------------------------- # add noisy data (noise equals standard deviation among change period # runs TODO could be replaced by "max_n_chgperiod_reps > 1" self.add_noisy_train_data = add_noisy_train_data self.n_noisy_series = 20 # number repetitions of the single change periods (at least 1 -> 1 run) # TODO insert into PSO self.max_n_chgperiod_reps = max_n_chperiod_reps # population for last generation of change period (for each run) # used for determining the EAs variance for change periods # 4d list [runs, chgperiods, parents, dims] # TODO insert into PSO self.final_pop_per_run_per_chgperiod = [ [] for _ in range(self.max_n_chgperiod_reps) ] # fitness of final population per run of each chgperiod # 3d list [runs, chgperiods, parents] # TODO insert into PSO self.final_pop_fitness_per_run_per_changeperiod = [ [] for _ in range(self.max_n_chgperiod_reps) ] # best found solution for each run of all change periods # format [runs, chgperiods, dims] self.best_found_pos_per_run_per_chgp = [ [] for _ in range(self.max_n_chgperiod_reps) ] # standard deviation and mean of the position of the best found solution # (computed over the change period runs) self.stddev_among_runs_per_chgp = [ [] for _ in range(self.max_n_chgperiod_reps) ] self.mean_among_runs_per_chgp = [ [] for _ in range(self.max_n_chgperiod_reps) ]