Parcourir la source

Merge branch 'release/v0.3.4' into master

Jérôme BUISINE il y a 4 ans
Parent
commit
28b986bcb7

+ 3 - 0
.gitmodules

@@ -4,3 +4,6 @@
 [submodule "wsao"]
 	path = wsao
 	url = https://gitlab.com/jbuisine/wsao.git
+[submodule "rnn"]
+	path = rnn
+	url = https://github.com/prise-3d/LSTM-noise-detection.git

Fichier diff supprimé car celui-ci est trop grand
+ 3752 - 0
OpenML_datasets/bio_response.csv


Fichier diff supprimé car celui-ci est trop grand
+ 3470 - 0
OpenML_datasets/gina_agnostic.csv


Fichier diff supprimé car celui-ci est trop grand
+ 4231 - 0
OpenML_datasets/hiva_agnostic.csv


Fichier diff supprimé car celui-ci est trop grand
+ 2601 - 0
OpenML_datasets/madelon.csv


+ 316 - 0
find_best_attributes_surrogate_dl.py

@@ -0,0 +1,316 @@
+# main imports
+import os
+import sys
+import argparse
+import pandas as pd
+import numpy as np
+import logging
+import datetime
+import random
+
+# model imports
+from sklearn.model_selection import train_test_split
+from sklearn.model_selection import GridSearchCV
+from sklearn.linear_model import LogisticRegression
+from sklearn.ensemble import RandomForestClassifier, VotingClassifier
+
+from keras.layers import Dense, Dropout, LSTM, Embedding, GRU, BatchNormalization
+from keras.preprocessing.sequence import pad_sequences
+from keras.models import Sequential
+
+import joblib
+import sklearn
+import sklearn.svm as svm
+from sklearn.utils import shuffle
+from sklearn.metrics import roc_auc_score
+from sklearn.model_selection import cross_val_score
+
+# modules and config imports
+sys.path.insert(0, '') # trick to enable import of main folder module
+
+import custom_config as cfg
+import models as mdl
+
+from optimization.ILSSurrogate import ILSSurrogate
+from macop.solutions.BinarySolution import BinarySolution
+
+from macop.operators.mutators.SimpleMutation import SimpleMutation
+from macop.operators.mutators.SimpleBinaryMutation import SimpleBinaryMutation
+from macop.operators.crossovers.SimpleCrossover import SimpleCrossover
+from macop.operators.crossovers.RandomSplitCrossover import RandomSplitCrossover
+
+from macop.operators.policies.UCBPolicy import UCBPolicy
+
+from macop.callbacks.BasicCheckpoint import BasicCheckpoint
+from macop.callbacks.UCBCheckpoint import UCBCheckpoint
+
+from sklearn.ensemble import RandomForestClassifier
+
+# variables and parameters
+models_list         = cfg.models_names_list
+
+def build_input(df):
+    """Convert dataframe to numpy array input with timesteps as float array
+    
+    Arguments:
+        df: {pd.Dataframe} -- Dataframe input
+    
+    Returns:
+        {np.ndarray} -- input LSTM data as numpy array
+    """
+
+    arr = df.to_numpy()
+
+    final_arr = []
+    for v in arr:
+        v_data = []
+        for vv in v:
+            #scaled_vv = np.array(vv, 'float') - np.mean(np.array(vv, 'float'))
+            #v_data.append(scaled_vv)
+            v_data.append(vv)
+        
+        final_arr.append(v_data)
+    
+    final_arr = np.array(final_arr, 'float32')            
+
+    return final_arr
+
+# default validator
+def validator(solution):
+
+    # at least 5 attributes
+    if list(solution.data).count(1) < 5:
+        return False
+
+    return True
+
+def create_model(input_shape):
+    print ('Creating model...')
+    model = Sequential()
+    #model.add(Embedding(input_dim = 1000, output_dim = 50, input_length=input_length))
+    model.add(LSTM(input_shape=input_shape, units=512, activation='tanh', recurrent_activation='sigmoid', dropout=0.4, return_sequences=True))
+    model.add(LSTM(units=128, activation='tanh', recurrent_activation='sigmoid', dropout=0.4, return_sequences=True))
+    model.add(LSTM(units=32, activation='tanh', dropout=0.4, recurrent_activation='sigmoid'))
+    model.add(Dense(1, activation='sigmoid'))
+
+    print ('Compiling...')
+    model.compile(loss='binary_crossentropy',
+                  optimizer='rmsprop',
+                  #metrics=['accuracy', tf.keras.metrics.AUC()])
+                  metrics=['accuracy'])
+
+    return model
+
+def loadDataset(filename):
+
+    # TODO : load data using DL RNN 
+
+    ########################
+    # 1. Get and prepare data
+    ########################
+    dataset_train = pd.read_csv(filename + '.train', header=None, sep=';')
+    dataset_test = pd.read_csv(filename + '.test', header=None, sep=';')
+
+    # getting weighted class over the whole dataset
+    # line is composed of :: [scene_name; zone_id; image_index_end; label; data]
+    noisy_df_train = dataset_train[dataset_train.iloc[:, 3] == 1]
+    not_noisy_df_train = dataset_train[dataset_train.iloc[:, 3] == 0]
+    nb_noisy_train = len(noisy_df_train.index)
+    nb_not_noisy_train = len(not_noisy_df_train.index)
+
+    noisy_df_test = dataset_test[dataset_test.iloc[:, 3] == 1]
+    not_noisy_df_test = dataset_test[dataset_test.iloc[:, 3] == 0]
+    nb_noisy_test = len(noisy_df_test.index)
+    nb_not_noisy_test = len(not_noisy_df_test.index)
+
+    noisy_samples = nb_noisy_test + nb_noisy_train
+    not_noisy_samples = nb_not_noisy_test + nb_not_noisy_train
+
+    total_samples = noisy_samples + not_noisy_samples
+
+    print('noisy', noisy_samples)
+    print('not_noisy', not_noisy_samples)
+    print('total', total_samples)
+
+    class_weight = {
+        0: noisy_samples / float(total_samples),
+        1: (not_noisy_samples / float(total_samples)),
+    }
+
+    # shuffle data
+    final_df_train = sklearn.utils.shuffle(dataset_train)
+    final_df_test = sklearn.utils.shuffle(dataset_test)
+
+    # split dataset into X_train, y_train, X_test, y_test
+    X_train_all = final_df_train.loc[:, 4:].apply(lambda x: x.astype(str).str.split(' '))
+    X_train_all = build_input(X_train_all)
+    y_train_all = final_df_train.loc[:, 3].astype('int')
+
+    X_test = final_df_test.loc[:, 4:].apply(lambda x: x.astype(str).str.split(' '))
+    X_test = build_input(X_test)
+    y_test = final_df_test.loc[:, 3].astype('int')
+
+    input_shape = (X_train_all.shape[1], X_train_all.shape[2])
+    print('Training data input shape', input_shape)
+
+    # prepare train and validation dataset
+    X_train, X_val, y_train, y_val = train_test_split(X_train_all, y_train_all, test_size=0.3, shuffle=False)
+
+    return X_train, X_val, y_train, y_val, X_test, y_test, class_weight
+
+
+def main():
+
+    parser = argparse.ArgumentParser(description="Train and find best filters to use for model")
+
+    parser.add_argument('--data', type=str, help='dataset filename prefix (without .train and .test)', required=True)
+    parser.add_argument('--start_surrogate', type=int, help='number of evalution before starting surrogare model', default=100)
+    parser.add_argument('--length', type=int, help='max data length (need to be specify for evaluator)', required=True)
+    parser.add_argument('--ils', type=int, help='number of total iteration for ils algorithm', required=True)
+    parser.add_argument('--ls', type=int, help='number of iteration for Local Search algorithm', required=True)
+    parser.add_argument('--output', type=str, help='output surrogate model name')
+
+    args = parser.parse_args()
+
+    p_data_file = args.data
+    p_length    = args.length
+    p_start     = args.start_surrogate
+    p_ils_iteration = args.ils
+    p_ls_iteration  = args.ls
+    p_output = args.output
+
+    print(p_data_file)
+
+    # load data from file
+    X_train, X_val, y_train, y_val, X_test, y_test, class_weight = loadDataset(p_data_file)
+
+    # create `logs` folder if necessary
+    if not os.path.exists(cfg.output_logs_folder):
+        os.makedirs(cfg.output_logs_folder)
+
+    logging.basicConfig(format='%(asctime)s %(message)s', filename='data/logs/{0}.log'.format(p_output), level=logging.DEBUG)
+
+    # init solution (`n` attributes)
+    def init():
+        return BinarySolution([], p_length).random(validator)
+
+    # define evaluate function here (need of data information)
+    def evaluate(solution):
+
+        start = datetime.datetime.now()
+
+        # get indices of filters data to use (filters selection from solution)
+        indices = []
+
+        for index, value in enumerate(solution.data): 
+            if value == 1: 
+                indices.append(index) 
+
+        # keep only selected filters from solution
+        x_train_filters = X_train[:, :, indices]
+        x_val_filters = X_val[:, :, indices]
+        x_test_filters = X_test[:, :, indices]
+        
+        # model = mdl.get_trained_model(p_choice, x_train_filters, y_train_filters)
+
+        # model = RandomForestClassifier(n_estimators=10)
+        input_shape = (x_train_filters.shape[1], x_train_filters.shape[2])
+        print('Training data input shape', input_shape)
+        model = create_model(input_shape)
+        model.summary()
+
+        # model = model.fit(x_train_filters, y_train_filters)
+
+        print("Fitting model with custom class_weight", class_weight)
+        history = model.fit(x_train_filters, y_train, batch_size=128, epochs=30, validation_data=(x_val_filters, y_val), verbose=1, shuffle=True, class_weight=class_weight)
+
+        
+        y_test_model = model.predict(x_test_filters)
+        y_test_predict = [ 1 if x > 0.5 else 0 for x in y_test_model ]
+        test_roc_auc = roc_auc_score(y_test, y_test_predict)
+
+        end = datetime.datetime.now()
+
+        diff = end - start
+
+        print("Real evaluation took: {}, score found: {}".format(divmod(diff.days * 86400 + diff.seconds, 60), test_roc_auc))
+
+        return test_roc_auc
+
+
+    # build all output folder and files based on `output` name
+    backup_model_folder = os.path.join(cfg.output_backup_folder, p_output)
+    surrogate_output_model = os.path.join(cfg.output_surrogates_model_folder, p_output)
+    surrogate_output_data = os.path.join(cfg.output_surrogates_data_folder, p_output)
+
+    if not os.path.exists(backup_model_folder):
+        os.makedirs(backup_model_folder)
+
+    if not os.path.exists(cfg.output_surrogates_model_folder):
+        os.makedirs(cfg.output_surrogates_model_folder)
+
+    if not os.path.exists(cfg.output_surrogates_data_folder):
+        os.makedirs(cfg.output_surrogates_data_folder)
+
+    backup_file_path = os.path.join(backup_model_folder, p_output + '.csv')
+    ucb_backup_file_path = os.path.join(backup_model_folder, p_output + '_ucbPolicy.csv')
+
+    # prepare optimization algorithm (only use of mutation as only ILS are used here, and local search need only local permutation)
+    operators = [SimpleBinaryMutation(), SimpleMutation()]
+    policy = UCBPolicy(operators)
+
+    # define first line if necessary
+    if not os.path.exists(surrogate_output_data):
+        folder, _ = os.path.split(surrogate_output_data)
+
+        if not os.path.exists(folder):
+            os.makedirs(folder)
+
+        with open(surrogate_output_data, 'w') as f:
+            f.write('x;y\n')
+
+    # custom ILS for surrogate use
+    algo = ILSSurrogate(_initalizer=init, 
+                        _evaluator=evaluate, # same evaluator by defadefaultult, as we will use the surrogate function
+                        _operators=operators, 
+                        _policy=policy, 
+                        _validator=validator,
+                        _surrogate_file_path=surrogate_output_model,
+                        _start_train_surrogate=p_start, # start learning and using surrogate after 1000 real evaluation
+                        _solutions_file=surrogate_output_data,
+                        _ls_train_surrogate=1,
+                        _maximise=True)
+    
+    algo.addCallback(BasicCheckpoint(_every=1, _filepath=backup_file_path))
+    algo.addCallback(UCBCheckpoint(_every=1, _filepath=ucb_backup_file_path))
+
+    bestSol = algo.run(p_ils_iteration, p_ls_iteration)
+
+    # print best solution found
+    print("Found ", bestSol)
+
+    # save model information into .csv file
+    if not os.path.exists(cfg.results_information_folder):
+        os.makedirs(cfg.results_information_folder)
+
+    filename_path = os.path.join(cfg.results_information_folder, cfg.optimization_attributes_result_filename)
+
+    filters_counter = 0
+    # count number of filters
+    for index, item in enumerate(bestSol.data):
+        if index != 0 and index % 2 == 1:
+
+            # if two attributes are used
+            if item == 1 or bestSol.data[index - 1] == 1:
+                filters_counter += 1
+
+
+    line_info = p_data_file + ';' + str(p_ils_iteration) + ';' + str(p_ls_iteration) + ';' + str(bestSol.data) + ';' + str(list(bestSol.data).count(1)) + ';' + str(filters_counter) + ';' + str(bestSol.fitness())
+    with open(filename_path, 'a') as f:
+        f.write(line_info + '\n')
+    
+    print('Result saved into %s' % filename_path)
+
+
+if __name__ == "__main__":
+    main()

+ 244 - 0
find_best_attributes_surrogate_openML.py

@@ -0,0 +1,244 @@
+# main imports
+import os
+import sys
+import argparse
+import pandas as pd
+import numpy as np
+import logging
+import datetime
+import random
+
+# model imports
+from sklearn.model_selection import train_test_split
+from sklearn.model_selection import GridSearchCV
+from sklearn.linear_model import LogisticRegression
+from sklearn.ensemble import RandomForestClassifier, VotingClassifier
+
+from keras.layers import Dense, Dropout, LSTM, Embedding, GRU, BatchNormalization
+from keras.preprocessing.sequence import pad_sequences
+from keras.models import Sequential
+
+import joblib
+import sklearn
+import sklearn.svm as svm
+from sklearn.utils import shuffle
+from sklearn.metrics import roc_auc_score
+from sklearn.model_selection import cross_val_score
+from sklearn.preprocessing import MinMaxScaler
+
+# modules and config imports
+sys.path.insert(0, '') # trick to enable import of main folder module
+
+import custom_config as cfg
+import models as mdl
+
+from optimization.ILSSurrogate import ILSSurrogate
+from macop.solutions.BinarySolution import BinarySolution
+
+from macop.operators.mutators.SimpleMutation import SimpleMutation
+from macop.operators.mutators.SimpleBinaryMutation import SimpleBinaryMutation
+from macop.operators.crossovers.SimpleCrossover import SimpleCrossover
+from macop.operators.crossovers.RandomSplitCrossover import RandomSplitCrossover
+
+from macop.operators.policies.UCBPolicy import UCBPolicy
+
+from macop.callbacks.BasicCheckpoint import BasicCheckpoint
+from macop.callbacks.UCBCheckpoint import UCBCheckpoint
+
+from sklearn.ensemble import RandomForestClassifier
+
+
+# default validator
+def validator(solution):
+
+    # at least 5 attributes
+    if list(solution.data).count(1) < 5:
+        return False
+
+    return True
+
+def train_model(X_train, y_train):
+
+    print ('Creating model...')
+    # here use of SVM with grid search CV
+    Cs = [0.001, 0.01, 0.1, 1, 10, 100, 1000]
+    gammas = [0.001, 0.01, 0.1, 5, 10, 100]
+    param_grid = {'kernel':['rbf'], 'C': Cs, 'gamma' : gammas}
+
+    svc = svm.SVC(probability=True, class_weight='balanced')
+    #clf = GridSearchCV(svc, param_grid, cv=5, verbose=1, scoring=my_accuracy_scorer, n_jobs=-1)
+    clf = GridSearchCV(svc, param_grid, cv=5, verbose=1, n_jobs=-1)
+
+    clf.fit(X_train, y_train)
+
+    model = clf.best_estimator_
+
+    return model
+
+def loadDataset(filename):
+
+    # TODO : load data using DL RNN 
+
+    ########################
+    # 1. Get and prepare data
+    ########################
+    dataset = pd.read_csv(filename, sep=',')
+
+    # change label as common
+    min_label_value = min(dataset.iloc[:, -1])
+    max_label_value = max(dataset.iloc[:, -1])
+
+    dataset.iloc[:, -1] = dataset.iloc[:, -1].replace(min_label_value, 0)
+    dataset.iloc[:, -1] = dataset.iloc[:, -1].replace(max_label_value, 1)
+
+    X_dataset = dataset.iloc[:, :-1]
+    y_dataset = dataset.iloc[:, -1]
+
+    problem_size = len(X_dataset.columns)
+
+    # min/max normalisation over feature
+    # create a scaler object
+    scaler = MinMaxScaler()
+    # fit and transform the data
+    X_dataset = np.array(pd.DataFrame(scaler.fit_transform(X_dataset), columns=X_dataset.columns))
+
+    # prepare train, validation and test datasets
+    X_train, X_test, y_train, y_test = train_test_split(X_dataset, y_dataset, test_size=0.3, shuffle=True)
+
+    return X_train, y_train, X_test, y_test, problem_size
+
+
+def main():
+
+    parser = argparse.ArgumentParser(description="Train and find best filters to use for model")
+
+    parser.add_argument('--data', type=str, help='open ml dataset filename prefix', required=True)
+    #parser.add_argument('--start_surrogate', type=int, help='number of evalution before starting surrogare model', default=100)
+    parser.add_argument('--ils', type=int, help='number of total iteration for ils algorithm', required=True)
+    parser.add_argument('--ls', type=int, help='number of iteration for Local Search algorithm', required=True)
+    parser.add_argument('--output', type=str, help='output surrogate model name')
+
+    args = parser.parse_args()
+
+    p_data_file = args.data
+    #p_start     = args.start_surrogate
+    p_ils_iteration = args.ils
+    p_ls_iteration  = args.ls
+    p_output = args.output
+
+    # load data from file and get problem size
+    X_train, y_train, X_test, y_test, problem_size = loadDataset(p_data_file)
+
+    # create `logs` folder if necessary
+    if not os.path.exists(cfg.output_logs_folder):
+        os.makedirs(cfg.output_logs_folder)
+
+    logging.basicConfig(format='%(asctime)s %(message)s', filename='data/logs/{0}.log'.format(p_output), level=logging.DEBUG)
+
+    # init solution (`n` attributes)
+    def init():
+        return BinarySolution([], problem_size).random(validator)
+
+    # define evaluate function here (need of data information)
+    def evaluate(solution):
+
+        start = datetime.datetime.now()
+
+        # get indices of filters data to use (filters selection from solution)
+        indices = []
+
+        for index, value in enumerate(solution.data): 
+            if value == 1: 
+                indices.append(index) 
+
+        # keep only selected filters from solution
+        x_train_filters = X_train[:, indices]
+        x_test_filters = X_test[ :, indices]
+        
+        # model = mdl.get_trained_model(p_choice, x_train_filters, y_train_filters)
+        model = train_model(x_train_filters, y_train)
+
+        y_test_model = model.predict(x_test_filters)
+        y_test_predict = [ 1 if x > 0.5 else 0 for x in y_test_model ]
+        test_roc_auc = roc_auc_score(y_test, y_test_predict)
+
+        end = datetime.datetime.now()
+
+        diff = end - start
+
+        print("Real evaluation took: {}, score found: {}".format(divmod(diff.days * 86400 + diff.seconds, 60), test_roc_auc))
+
+        return test_roc_auc
+
+
+    # build all output folder and files based on `output` name
+    backup_model_folder = os.path.join(cfg.output_backup_folder, p_output)
+    surrogate_output_model = os.path.join(cfg.output_surrogates_model_folder, p_output)
+    surrogate_output_data = os.path.join(cfg.output_surrogates_data_folder, p_output)
+
+    if not os.path.exists(backup_model_folder):
+        os.makedirs(backup_model_folder)
+
+    if not os.path.exists(cfg.output_surrogates_model_folder):
+        os.makedirs(cfg.output_surrogates_model_folder)
+
+    if not os.path.exists(cfg.output_surrogates_data_folder):
+        os.makedirs(cfg.output_surrogates_data_folder)
+
+    backup_file_path = os.path.join(backup_model_folder, p_output + '.csv')
+    ucb_backup_file_path = os.path.join(backup_model_folder, p_output + '_ucbPolicy.csv')
+
+    # prepare optimization algorithm (only use of mutation as only ILS are used here, and local search need only local permutation)
+    operators = [SimpleBinaryMutation(), SimpleMutation()]
+    policy = UCBPolicy(operators)
+
+    # define first line if necessary
+    if not os.path.exists(surrogate_output_data):
+        folder, _ = os.path.split(surrogate_output_data)
+
+        if not os.path.exists(folder):
+            os.makedirs(folder)
+
+        with open(surrogate_output_data, 'w') as f:
+            f.write('x;y\n')
+
+
+    # custom start surrogate variable based on problem size
+    p_start = int(problem_size)
+    print(f'Starting using surrogate after {p_start} reals training')
+
+    # custom ILS for surrogate use
+    algo = ILSSurrogate(_initalizer=init, 
+                        _evaluator=evaluate, # same evaluator by defadefaultult, as we will use the surrogate function
+                        _operators=operators, 
+                        _policy=policy, 
+                        _validator=validator,
+                        _surrogate_file_path=surrogate_output_model,
+                        _start_train_surrogate=p_start, # start learning and using surrogate after 1000 real evaluation
+                        _solutions_file=surrogate_output_data,
+                        _ls_train_surrogate=1,
+                        _maximise=True)
+    
+    algo.addCallback(BasicCheckpoint(_every=1, _filepath=backup_file_path))
+    algo.addCallback(UCBCheckpoint(_every=1, _filepath=ucb_backup_file_path))
+
+    bestSol = algo.run(p_ils_iteration, p_ls_iteration)
+
+    # print best solution found
+    print("Found ", bestSol)
+
+    # save model information into .csv file
+    if not os.path.exists(cfg.results_information_folder):
+        os.makedirs(cfg.results_information_folder)
+
+    filename_path = os.path.join(cfg.results_information_folder, cfg.optimization_attributes_result_filename)
+
+    line_info = p_data_file + ';' + str(p_ils_iteration) + ';' + str(p_ls_iteration) + ';' + str(bestSol.data) + ';' + str(list(bestSol.data).count(1)) + ';' + str(bestSol.fitness())
+    with open(filename_path, 'a') as f:
+        f.write(line_info + '\n')
+    
+    print('Result saved into %s' % filename_path)
+
+
+if __name__ == "__main__":
+    main()

+ 44 - 9
optimization/ILSSurrogate.py

@@ -140,20 +140,37 @@ class ILSSurrogate(Algorithm):
         # by default use of mother method to initialize variables
         super().run(_evaluations)
 
+        # initialize current solution
+        self.initRun()
+
         # enable resuming for ILS
         self.resume()
 
-        if self.start_train_surrogate < self.getGlobalEvaluation():
-            self.load_surrogate()
+        if self.start_train_surrogate > self.getGlobalEvaluation():
+        
+            # get `self.start_train_surrogate` number of real evaluations and save it into surrogate dataset file
+            # using randomly generated solutions (in order to cover seearch space)
+            while self.start_train_surrogate > self.getGlobalEvaluation():
+                
+                newSolution = self.initializer()
+
+                # evaluate new solution
+                newSolution.evaluate(self.evaluator)
 
-        # initialize current solution
-        self.initRun()
+                # add it to surrogate pool
+                self.add_to_surrogate(newSolution)
+
+                self.increaseEvaluation()
+
+        # train surrogate on real evaluated solutions file
+        self.train_surrogate()
+        self.load_surrogate()
 
         # local search algorithm implementation
         while not self.stop():
             
             # set current evaluator based on used or not of surrogate function
-            current_evaluator = self.surrogate_evaluator if self.start_train_surrogate < self.getGlobalEvaluation() else self.evaluator
+            current_evaluator = self.surrogate_evaluator if self.start_train_surrogate <= self.getGlobalEvaluation() else self.evaluator
 
             # create new local search instance
             # passing global evaluation param from ILS
@@ -173,12 +190,14 @@ class ILSSurrogate(Algorithm):
             newSolution = ls.run(_ls_evaluations)
 
             # if better solution than currently, replace it (solution saved in training pool, only if surrogate process is in a second process step)
-            if self.isBetter(newSolution) and self.start_train_surrogate < self.getGlobalEvaluation():
+            # Update : always add new solution into surrogate pool, not only if solution is better
+            #if self.isBetter(newSolution) and self.start_train_surrogate < self.getGlobalEvaluation():
+            if self.start_train_surrogate <= self.getGlobalEvaluation():
 
                 # if better solution found from local search, retrained the found solution and test again
                 # without use of surrogate
                 fitness_score = self.evaluator(newSolution)
-                self.increaseEvaluation()
+                # self.increaseEvaluation() # dot not add evaluation
 
                 newSolution.score = fitness_score
 
@@ -188,9 +207,10 @@ class ILSSurrogate(Algorithm):
 
                 self.add_to_surrogate(newSolution)
 
+                self.progress()
 
             # check if necessary or not to train again surrogate
-            if self.n_local_search % self.ls_train_surrogate == 0 and self.start_train_surrogate < self.getGlobalEvaluation():
+            if self.n_local_search % self.ls_train_surrogate == 0 and self.start_train_surrogate <= self.getGlobalEvaluation():
 
                 # train again surrogate on real evaluated solutions file
                 self.train_surrogate()
@@ -207,4 +227,19 @@ class ILSSurrogate(Algorithm):
                      (type(self).__name__, self.bestSolution))
 
         self.end()
-        return self.bestSolution
+        return self.bestSolution
+
+    def addCallback(self, _callback):
+        """Add new callback to algorithm specifying usefull parameters
+
+        Args:
+            _callback: {Callback} -- specific Callback instance
+        """
+        # specify current main algorithm reference
+        if self.parent is not None:
+            _callback.setAlgo(self.parent)
+        else:
+            _callback.setAlgo(self)
+
+        # set as new
+        self.callbacks.append(_callback)

+ 27 - 7
optimization/LSSurrogate.py

@@ -22,22 +22,23 @@ class LocalSearchSurrogate(Algorithm):
         bestSolution: {Solution} -- best solution found so far during running algorithm
         callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initializing algorithm
     """
-    def run(self, _evaluations):
+    def run(self, evaluations):
         """
         Run the local search algorithm
 
         Args:
-            _evaluations: {int} -- number of Local search evaluations
+            evaluations: {int} -- number of Local search evaluations
             
         Returns:
             {Solution} -- best solution found
         """
 
         # by default use of mother method to initialize variables
-        super().run(_evaluations)
+        super().run(evaluations)
 
-        if self.parent:
-            self.bestSolution = self.parent.bestSolution
+        # do not use here the best solution known (default use of initRun and current solution)
+        # if self.parent:
+        #     self.bestSolution = self.parent.bestSolution
 
         # initialize current solution
         self.initRun()
@@ -60,18 +61,37 @@ class LocalSearchSurrogate(Algorithm):
                 self.increaseEvaluation()
 
                 self.progress()
+
                 logging.info("---- Current %s - SCORE %s" %
                              (newSolution, newSolution.fitness()))
 
                 # add to surrogate pool file if necessary (using ILS parent reference)
-                if self.parent.start_train_surrogate >= self.getGlobalEvaluation():
-                    self.parent.add_to_surrogate(newSolution)
+                # if self.parent.start_train_surrogate >= self.getGlobalEvaluation():
+                #     self.parent.add_to_surrogate(newSolution)
 
                 # stop algorithm if necessary
                 if self.stop():
                     break
 
+            # after applying local search on currentSolution, we switch into new local area using known current bestSolution
+            self.currentSolution = self.bestSolution
+
         logging.info("End of %s, best solution found %s" %
                      (type(self).__name__, self.bestSolution))
 
         return self.bestSolution
+
+    def addCallback(self, callback):
+        """Add new callback to algorithm specifying usefull parameters
+
+        Args:
+            callback: {Callback} -- specific Callback instance
+        """
+        # specify current main algorithm reference
+        if self.parent is not None:
+            callback.setAlgo(self.parent)
+        else:
+            callback.setAlgo(self)
+
+        # set as new
+        self.callbacks.append(callback)

+ 1 - 0
rnn

@@ -0,0 +1 @@
+Subproject commit c4acf38ab3816725faa0bb84c68cb18fdd5ebb32

+ 35 - 0
run_openML_surrogate.py

@@ -0,0 +1,35 @@
+import os, argparse
+
+open_ml_problems_folder = 'OpenML_datasets'
+
+def main():
+
+    parser = argparse.ArgumentParser(description="Find best features for each OpenML problems")
+
+    parser.add_argument('--ils', type=int, help='number of total iteration for ils algorithm', required=True)
+    parser.add_argument('--ls', type=int, help='number of iteration for Local Search algorithm', required=True)
+
+    args = parser.parse_args()
+
+    p_ils = args.ils
+    p_ls  = args.ls
+
+    open_ml_problems = os.listdir(open_ml_problems_folder)
+
+    for ml_problem in open_ml_problems:
+
+        ml_problem_name = ml_problem.replace('.csv', '')
+        ml_problem_path = os.path.join(open_ml_problems_folder, ml_problem)
+
+        ml_surrogate_command = f"python find_best_attributes_surrogate_openML.py " \
+                               f"--data {ml_problem_path} " \
+                               f"--ils {p_ils} " \
+                               f"--ls {p_ls} " \
+                               f"--output {ml_problem_name}"
+        print(f'Run surrogate features selection for {ml_problem_name} with [ils: {p_ils}, ls: {p_ls}]')
+        print(ml_surrogate_command)
+        os.system(ml_surrogate_command)
+    
+
+if __name__ == "__main__":
+    main()