callbacks.rst 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. Keep track
  2. ==============
  3. Keeping track of the running algorithm can be useful on two levels. First of all to understand how it unfolded at the end of the classic run. But also in the case of the unwanted shutdown of the algorithm.
  4. This section will allow you to introduce the recovery of the algorithm thanks to a continuous backup functionality.
  5. Logging into algorithm
  6. ~~~~~~~~~~~~~~~~~~~~~~
  7. Some logs can be retrieve after running an algorithm. **Macop** uses the ``logging`` Python package in order to log algorithm advancement.
  8. Here is an example of use when running an algorithm:
  9. .. code-block:: python
  10. """
  11. basic imports
  12. """
  13. import logging
  14. # logging configuration
  15. logging.basicConfig(format='%(asctime)s %(message)s', filename='data/example.log', level=logging.DEBUG)
  16. ...
  17. # maximizing algorithm (relative to knapsack problem)
  18. algo = HillClimberBestImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
  19. # run the algorithm using local search and get solution found
  20. solution = algo.run(evaluations=100)
  21. print(solution.fitness)
  22. Hence, log data are saved into ``data/example.log`` in our example.
  23. Callbacks introduction
  24. ~~~~~~~~~~~~~~~~~~~~~~~
  25. Having output logs can help to understand an error that has occurred, however all the progress of the research carried out may be lost.
  26. For this, the functionality relating to callbacks has been developed.
  27. Within **Macop**, a callback is a specific instance of ``macop.callbacks.Callback`` that allows you to perform an action of tracing / saving information **every** ``n`` **evaluations** but also reloading information if necessary when restarting an algorithm.
  28. .. code-block:: python
  29. class Callback():
  30. def __init__(self, every, filepath):
  31. ...
  32. @abstractmethod
  33. def run(self):
  34. """
  35. Check if necessary to do backup based on `every` variable
  36. """
  37. pass
  38. @abstractmethod
  39. def load(self):
  40. """
  41. Load last backup line of solution and set algorithm state at this backup
  42. """
  43. pass
  44. def setAlgo(self, algo):
  45. """
  46. Specify the main algorithm instance reference
  47. """
  48. ...
  49. - The ``run`` method will be called during run process of the algo and do backup at each specific number of evaluations.
  50. - The ``load`` method will be used to reload the state of the algorithm from the last information saved. All saved data is saved in a file whose name will be specified by the user.
  51. Towards the use of Callbacks
  52. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  53. We are going to create our own Callback instance called ``BasicCheckpoint`` which will save the best solution found and number of evaluations done in order to reload it for the next run of our algorithm.
  54. .. code-block:: python
  55. """
  56. module imports
  57. """
  58. from macop.callbacks.base import Callback
  59. class BasicCheckpoint(Callback):
  60. def run(self):
  61. """
  62. Check if necessary to do backup based on `every` variable
  63. """
  64. # get current best solution
  65. solution = self.algo._bestSolution
  66. currentEvaluation = self.algo.getGlobalEvaluation()
  67. # backup if necessary every number of evaluations
  68. if currentEvaluation % self._every == 0:
  69. # create specific line with solution data
  70. solution.data = ""
  71. solutionSize = len(solution.getdata = ))
  72. for index, val in enumerate(solution.getdata = )):
  73. solution.data += str(val)
  74. if index < solutionSize - 1:
  75. solution.data += ' '
  76. # number of evaluations done, solution data and fitness score
  77. line = str(currentEvaluation) + ';' + solution.data + ';' + str(
  78. solution.fitness) + ';\n'
  79. # check if file exists
  80. if not os.path.exists(self._filepath):
  81. with open(self._filepath, 'w') as f:
  82. f.write(line)
  83. else:
  84. with open(self._filepath, 'a') as f:
  85. f.write(line)
  86. def load(self):
  87. """
  88. Load last backup line and set algorithm state (best solution and evaluations)
  89. """
  90. if os.path.exists(self._filepath):
  91. with open(self._filepath) as f:
  92. # get last line and read data
  93. lastline = f.readlines()[-1]
  94. data = lastline.split(';')
  95. # get evaluation information
  96. globalEvaluation = int(data[0])
  97. # restore number of evaluations
  98. if self.algo.getParent() is not None:
  99. self.algo.getParent()._numberOfEvaluations = globalEvaluation
  100. else:
  101. self.algo._numberOfEvaluations = globalEvaluation
  102. # get best solution data information
  103. solution.data = list(map(int, data[1].split(' ')))
  104. # avoid uninitialised solution
  105. if self.algo._bestSolution is None:
  106. self.algo._bestSolution = self.algo.initialiser()
  107. # set to algorithm the lastest obtained best solution
  108. self.algo._bestsolution.getdata = ) = np.array(solution.data)
  109. self.algo._bestSolution._score = float(data[2])
  110. In this way, it is possible to specify the use of a callback to our algorithm instance:
  111. .. code-block:: python
  112. ...
  113. # maximizing algorithm (relative to knapsack problem)
  114. algo = HillClimberBestImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
  115. callback = BasicCheckpoint(every=5, filepath='data/hillClimberBackup.csv')
  116. # add callback into callback list
  117. algo.addCallback(callback)
  118. # run the algorithm using local search and get solution found
  119. solution = algo.run(evaluations=100)
  120. print(solution.fitness)
  121. .. note::
  122. It is possible to add as many callbacks as desired in the algorithm in question.
  123. Previously, some methods of the abstract ``Algorithm`` class have not been presented. These methods are linked to the use of callbacks,
  124. in particular the ``addCallback`` method which allows the addition of a callback to an algorithm instance as seen above.
  125. - The ``resume`` method will reload all callbacks list using ``load`` method.
  126. - The ``progress`` method will ``run`` each callbacks during the algorithm search.
  127. If we want to exploit this functionality, then we will need to exploit them within our algorithm. Let's make the necessary modifications for our algorithm ``IteratedLocalSearch``:
  128. .. code-block:: python
  129. """
  130. module imports
  131. """
  132. from macop.algorithms.base import Algorithm
  133. class IteratedLocalSearch(Algorithm):
  134. ...
  135. def run(self, evaluations, ls_evaluations=100):
  136. """
  137. Run the iterated local search algorithm using local search
  138. """
  139. # by default use of mother method to initialise variables
  140. super().run(evaluations)
  141. # initialise current solution
  142. self.initRun()
  143. # restart using callbacks backup list
  144. self.resume()
  145. # local search algorithm implementation
  146. while not self.stop():
  147. # create and search solution from local search
  148. newSolution = self._localSearch.run(ls_evaluations)
  149. # if better solution than currently, replace it
  150. if self.isBetter(newSolution):
  151. self._bestSolution = newSolution
  152. # check if necessary to call each callbacks
  153. self.progress()
  154. self.information()
  155. return self._bestSolution
  156. All the features of **Macop** were presented. The next section will aim to quickly present the few implementations proposed within **Macop** to highlight the modulality of the package.