ILSSurrogate.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. """Iterated Local Search Algorithm implementation using surrogate as fitness approximation
  2. """
  3. # main imports
  4. import os
  5. import logging
  6. import joblib
  7. # module imports
  8. from macop.algorithms.Algorithm import Algorithm
  9. from macop.algorithms.mono.LocalSearch import LocalSearch
  10. from sklearn.linear_model import (LinearRegression, Lasso, Lars, LassoLars,
  11. LassoCV, ElasticNet)
  12. from wsao.sao.problems.nd3dproblem import ND3DProblem
  13. from wsao.sao.surrogates.walsh import WalshSurrogate
  14. from wsao.sao.algos.fitter import FitterAlgo
  15. from wsao.sao.utils.analysis import SamplerAnalysis, FitterAnalysis, OptimizerAnalysis
  16. class ILSSurrogate(Algorithm):
  17. """Iterated Local Search used to avoid local optima and increave EvE (Exploration vs Exploitation) compromise using surrogate
  18. Attributes:
  19. initalizer: {function} -- basic function strategy to initialize solution
  20. evaluator: {function} -- basic function in order to obtained fitness (mono or multiple objectives)
  21. operators: {[Operator]} -- list of operator to use when launching algorithm
  22. policy: {Policy} -- Policy class implementation strategy to select operators
  23. validator: {function} -- basic function to check if solution is valid or not under some constraints
  24. maximise: {bool} -- specify kind of optimization problem
  25. currentSolution: {Solution} -- current solution managed for current evaluation
  26. bestSolution: {Solution} -- best solution found so far during running algorithm
  27. ls_iteration: {int} -- number of evaluation for each local search algorithm
  28. surrogate_file: {str} -- Surrogate model file to load (model trained using https://gitlab.com/florianlprt/wsao)
  29. surrogate: {Surrogate} -- Surrogate model instance loaded
  30. ls_train_surrogate: {int} -- Specify if we need to retrain our surrogate model (every Local Search)
  31. solutions_file: {str} -- Path where real evaluated solutions are saved in order to train surrogate again
  32. real_evaluator: {function} -- real expected evaluation to use
  33. callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initializing algorithm
  34. """
  35. def __init__(self,
  36. _initalizer,
  37. _evaluator,
  38. _operators,
  39. _policy,
  40. _validator,
  41. _surrogate_file_path,
  42. _ls_train_surrogate,
  43. _solutions_file,
  44. _real_evaluator,
  45. _maximise=True,
  46. _parent=None):
  47. super().__init__(_initalizer, _evaluator, _operators, _policy,
  48. _validator, _maximise, _parent)
  49. self.n_local_search = 0
  50. self.surrogate_file_path = _surrogate_file_path
  51. self.load_surrogate()
  52. self.real_evaluator = _real_evaluator
  53. self.ls_train_surrogate = _ls_train_surrogate
  54. self.solutions_file = _solutions_file
  55. def train_surrogate(self):
  56. """etrain if necessary the whole surrogate fitness approximation function
  57. """
  58. # Following https://gitlab.com/florianlprt/wsao, we re-train the model
  59. # ---------------------------------------------------------------------------
  60. # cli_restart.py problem=nd3d,size=30,filename="data/statistics_extended_svdn" \
  61. # model=lasso,alpha=1e-5 \
  62. # surrogate=walsh,order=3 \
  63. # algo=fitter,algo_restarts=10,samplefile=stats_extended.csv \
  64. # sample=1000,step=10 \
  65. # analysis=fitter,logfile=out_fit.csv
  66. problem = ND3DProblem(size=len(self.bestSolution.data)) # problem size based on best solution size (need to improve...)
  67. model = Lasso(alpha=1e-5)
  68. surrogate = WalshSurrogate(order=3, size=problem.size, model=model)
  69. analysis = FitterAnalysis(logfile="train_surrogate.log", problem=problem)
  70. algo = FitterAlgo(problem=problem, surrogate=surrogate, analysis=analysis, seed=problem.seed)
  71. print("Start fitting again the surrogate model")
  72. for r in range(10):
  73. print("Iteration n°{0}: for fitting surrogate".format(r))
  74. algo.run(samplefile=self.solutions_file, sample=100, step=10)
  75. joblib.dump(algo, self.surrogate_file_path)
  76. def load_surrogate(self):
  77. """Load algorithm with surrogate model and create lambda evaluator function
  78. """
  79. # need to first train surrogate if not exist
  80. if not os.path.exists(self.surrogate_file_path):
  81. self.train_surrogate()
  82. self.surrogate = joblib.load(self.surrogate_file_path)
  83. # update evaluator function
  84. self.evaluator = lambda s: self.surrogate.surrogate.predict([s.data])[0]
  85. def run(self, _evaluations, _ls_evaluations=100):
  86. """
  87. Run the iterated local search algorithm using local search (EvE compromise)
  88. Args:
  89. _evaluations: {int} -- number of global evaluations for ILS
  90. _ls_evaluations: {int} -- number of Local search evaluations (default: 100)
  91. Returns:
  92. {Solution} -- best solution found
  93. """
  94. # by default use of mother method to initialize variables
  95. super().run(_evaluations)
  96. # enable resuming for ILS
  97. self.resume()
  98. # initialize current solution
  99. self.initRun()
  100. # local search algorithm implementation
  101. while not self.stop():
  102. # create new local search instance
  103. # passing global evaluation param from ILS
  104. ls = LocalSearch(self.initializer,
  105. self.evaluator,
  106. self.operators,
  107. self.policy,
  108. self.validator,
  109. self.maximise,
  110. _parent=self)
  111. # add same callbacks
  112. for callback in self.callbacks:
  113. ls.addCallback(callback)
  114. # create and search solution from local search
  115. newSolution = ls.run(_ls_evaluations)
  116. # if better solution than currently, replace it
  117. if self.isBetter(newSolution):
  118. # if better solution found from local search, retrained the found solution and test again
  119. # without use of surrogate
  120. fitness_score = self.real_evaluator(newSolution)
  121. self.increaseEvaluation()
  122. newSolution.score = fitness_score
  123. # if solution is really better after real evaluation, then we replace
  124. if self.isBetter(newSolution):
  125. self.bestSolution = newSolution
  126. # save real evaluated solution into specific file for surrogate
  127. with open(self.solutions_file, 'a') as f:
  128. line = ""
  129. for index, e in enumerate(newSolution.data):
  130. line += str(e)
  131. if index < len(newSolution.data) - 1:
  132. line += ","
  133. line += ";"
  134. line += str(newSolution.score)
  135. f.write(line + "\n")
  136. # check if necessary or not to train again surrogate
  137. if self.n_local_search % self.ls_train_surrogate == 0:
  138. # train again surrogate on real evaluated solutions file
  139. self.train_surrogate()
  140. # reload new surrogate function
  141. self.load_surrogate()
  142. # increase number of local search done
  143. self.n_local_search += 1
  144. self.information()
  145. logging.info("End of %s, best solution found %s" %
  146. (type(self).__name__, self.bestSolution))
  147. self.end()
  148. return self.bestSolution