Scipy.optimize и обратный вызов не работает при использовании работников - PullRequest
0 голосов
/ 24 сентября 2019

Я использую scipy.optimize ( дифференциальная эволюция ) для оптимизации.В целом работает отлично.Я начал использовать функцию работника (которая позволяет указывать оптимизации для выделения отдельных лиц из популяции в другие процессы, очень легко эффективно распараллеливая процесс оптимизации).

Я вижу, что если я укажу работников= -1, все мои ядра работают должным образом.

Симуляция управляется в структуре:

""" Sandbox stuff """
from scipy.optimize import *

class Simulation:
    def __init__(self):
        self.data = None

    def optim_manager(self):
        optimized_results = differential_evolution(self.optim_loop, 
        bounds=self.bounds, callback=self.optim_callback, workers=-1)

    def optim_loop(self, parameters):
        self.data = run_model(parameters)
        optim_value = self.calc_optim_value(self.data)

        # at this point self.data exist & is not None
        return optim_value


    def optim_callback(self,*args):
        # *args contains the parameters value for that optimal run
        optim_value = self.calc_optim_value(self.data)              # error
        write_optimisation_run_to_dictionnary(args, optim_value)

Однако, когда я пытаюсь вычислить optim_value вобратный вызов я получаю:

Traceback (most recent call last):
  File "lancer_modele.py", line 512, in <module>
    mp.launch()
  File "lancer_modele.py", line 234, in launch
    self.do_optim_ghosts()
  File "lancer_modele.py", line 296, in do_optim_ghosts
    opt.opti_manager_ghosts(**merged_kwargs)
  File "/home/atari/PycharmProjects/RT/managers/optimisation.py", line 41, in opti_manager_ghosts
    simul_params = simul.fctopti_ghosts(nbrite=1, free_params_ordereddict=free_init_params[-1], **kwargs)   # it returns other stuff we don't need, a,b,c
  File "/home/atari/PycharmProjects/RT/managers/Simulation.py", line 225, in fctopti_ghosts
    popsize=5, callback=self.optim_callback, workers=2)
  File "/home/atari/.local/lib/python3.6/site-packages/scipy/optimize/_differentialevolution.py", line 272, in differential_evolution
    ret = solver.solve()
  File "/home/atari/.local/lib/python3.6/site-packages/scipy/optimize/_differentialevolution.py", line 701, in solve
    convergence=self.tol / convergence) is True):
  File "/home/atari/PycharmProjects/RT/managers/Simulation.py", line 433, in optim_callback
    optim_value = self.calc_ghost_optimized_value()
  File "/home/atari/PycharmProjects/RT/managers/Simulation.py", line 509, in calc_ghost_optimized_value
    optim_value = np.sum(cubedata[:,:,self.gstart_tick:self.gstop_tick])
TypeError: 'NoneType' object is not subscriptable

, поскольку self.data имеет значение None в контексте optim_callback.Это работает нормально, однако, если я использую только 1 ядро.Я предполагаю, что при использовании нескольких ядер класс сериализуется и передается, а атрибуты обновляются в каждом запущенном процессе.Затем он возвращается к основному процессу, где Simulation все еще остается прежним, потому что был вызван дополнительный ответчик (поэтому он все еще имеет self.data = None).Это означает, что я не могу легко вычислить значение optim, и кажется, что хотя обратный вызов scipy и дает мне значения параметров в порядке, он не передает значение, которое я сказал, что они стоили в моей модели.

Anyидея?Я думаю, что я мог бы отдельно заново создать полную модель в optim_callback, обновить ее с предоставленными параметрами, запустить ее снова, чтобы получить значение оптимизации.Тем не менее, модель работает довольно долго, и я бы не стал замедлять оптимизацию каждый раз, когда получаю (промежуточное) оптимальное значение.

1 Ответ

0 голосов
/ 27 сентября 2019

На самом деле, похоже, нет никаких причин, по которым DifferentialEvolution не возвращает значение оптимизации.Следовательно, можно создать подкласс

from scipy.optimize._differentialevolution import DifferentialEvolutionSolver

И нужно всего лишь отредактировать несколько строк в DifferentialEvolutionSolver.solve () в строке 697:

     if (self.callback and
             self.callback(self._scale_parameters(self.population[0]),
                           convergence=self.tol / convergence) is True):

Становится:

    if (self.callback and
            self.callback(self._scale_parameters(self.population[0]),
                          convergence=self.tol / convergence, optim_value=self.population_energies[0]) is True):

Поскольку self.population_energies до сих пор содержит значения оптимизации, причем [0] является наилучшим значением в этой точке.

Поэтому в методе обратного вызова:

def optim_callback(self,*args, **kwargs):
    # *args contains the parameters value for that optimal run
    optim_value = kwargs['optim_value']
    write_optimisation_run_to_dictionnary(args, optim_value)

Обеспечитзначение оптимизации без необходимости перезапуска модели.

...