You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I am using the parallel processing feature and am encountering and issue where if I define the fitness function using closure in a class (to pass additional data whilst respecting the number of arguments the function can take) and use parallel processing with the "process" flag, no errors are raised and the program just stays stuck indefinitely. This does not happen when using "thread" or when not using parallel processing.
I have made a simple example of the setup I have below to show how I define my fitness function.
importpygadclassTest:
def__init__(self):
self.data=5""" Input data needed for fitness function here """defouter_fitness_func(self):
deffitness_func(solution, sol_idx):
"""calculations"""fitness_value=self.datareturnfitness_valuereturnfitness_functest=Test()
ga_instance=pygad.GA(num_generations=10,
num_parents_mating=3,
sol_per_pop=5,
num_genes=10,
fitness_func=test.outer_fitness_func(),
parallel_processing=["process", 2])
if__name__=='__main__':
ga_instance.run()
Not using "process" is not feasible for my application as it significantly improves the performance. My current fix is to allow an extra argument to be passed in the fitness function and pass all my data through that. However, this is not very clean and I can still not include my fitness function into a class. Any solutions to this?
Please note that in your example you are setting fitness_func to a call rather than to a reference of the fitness function.
Change fitness_func=test.outer_fitness_func() to fitness_func=test.outer_fitness_func.
In your case neither "thread", "process", or "None" should work.
Please try this change and post the outcome if that does not solve your problem.
I do believe though that using test.outer_fitness_func() is the correct way of proceeding as I am using a closure to wrap around the fitness function. If I were to set fitness_func=test.outer_fitness_func, I would be passing the outer function as the fitness function which is not the intended behaviour and it would return a method instead of the fitness value during calculations.
Sorry Alexis, I did not pay attention to your fitness function. You are correct.
Then, looking at pygad.py source code lines 1259-1278, is where parallel processing is executed. Whether "thread" or "process" is the same code, with the exception of ExecutorClass refering to ThreadPoolExecutor or ProcessPoolExecutor, correspondingly. So it should work the same way.
However, you indicated that "This does not happen when using "thread" or when not using parallel processing". If that is the case, I would (logically) surmise that it is working with "process" too but the return fitness_value ends in a separate process space. It probably looks like it is hanging because it is. The pygad method is still waiting for the fitness value to be returned. However, it was returned but in a different memory space.
I am somewhat curious and would try to replicate this over the weekend. Perhaps changing the code executor.map() to executor.submit() would at least give more control over the process and to query its status. The executor.map() is simply a multiprocess version of Python's map(); with not much control over it.
BTW, I use multiprocessing for speed-up too, but I wrap the entire pygad.GA() call instead of using its parallel_processing option.
Alexis, you need to add a global declaration to the closure method as below. As suspected, it was a memory space issue, not exactly the way I thought but still a namespace issue. Under multiprocessing, your local space is not visible, while under threading it is all in the same memory space.
def outer_fitness_func(self):
global fitness_func
def fitness_func(solution, sol_idx):
I also get somes problems using multithreading. but I've found this piece of code wich have been writen a few month before the feature parallel_processing.
This is not an issue in PyGAD. I just used Keras with multiprocessing and the issue exists. This is a reproducible example. I set a random seed to NumPy to make sure that there is no randomness across the different runs.
Activity
borisarloff commentedon Nov 30, 2022
Please note that in your example you are setting fitness_func to a call rather than to a reference of the fitness function.
Change fitness_func=test.outer_fitness_func() to fitness_func=test.outer_fitness_func.
In your case neither "thread", "process", or "None" should work.
Please try this change and post the outcome if that does not solve your problem.
Aleksy9 commentedon Dec 1, 2022
Thank you for your response Boris!
I do believe though that using
test.outer_fitness_func()is the correct way of proceeding as I am using a closure to wrap around the fitness function. If I were to setfitness_func=test.outer_fitness_func, I would be passing the outer function as the fitness function which is not the intended behaviour and it would return a method instead of the fitness value during calculations.borisarloff commentedon Dec 1, 2022
Sorry Alexis, I did not pay attention to your fitness function. You are correct.
Then, looking at pygad.py source code lines 1259-1278, is where parallel processing is executed. Whether "thread" or "process" is the same code, with the exception of ExecutorClass refering to ThreadPoolExecutor or ProcessPoolExecutor, correspondingly. So it should work the same way.
However, you indicated that "This does not happen when using "thread" or when not using parallel processing". If that is the case, I would (logically) surmise that it is working with "process" too but the return fitness_value ends in a separate process space. It probably looks like it is hanging because it is. The pygad method is still waiting for the fitness value to be returned. However, it was returned but in a different memory space.
I am somewhat curious and would try to replicate this over the weekend. Perhaps changing the code executor.map() to executor.submit() would at least give more control over the process and to query its status. The executor.map() is simply a multiprocess version of Python's map(); with not much control over it.
BTW, I use multiprocessing for speed-up too, but I wrap the entire pygad.GA() call instead of using its parallel_processing option.
borisarloff commentedon Dec 3, 2022
Alexis, you need to add a global declaration to the closure method as below. As suspected, it was a memory space issue, not exactly the way I thought but still a namespace issue. Under multiprocessing, your local space is not visible, while under threading it is all in the same memory space.
def outer_fitness_func(self):
BenoitMiquey commentedon Dec 14, 2022
Hi @Aleksy9 ,
I also get somes problems using multithreading. but I've found this piece of code wich have been writen a few month before the feature parallel_processing.
Try this .
This solution works to fix problems in my issue.
ahmedfgad commentedon Mar 1, 2023
This is not an issue in PyGAD. I just used Keras with
multiprocessingand the issue exists. This is a reproducible example. I set a random seed to NumPy to make sure that there is no randomness across the different runs.I also posted a question at StackOverflow: https://stackoverflow.com/questions/75606326/python-parallel-processing-with-threadpoolexecutor-gives-wrong-results-with-kera
This is the correct model outputs without using parallel processing:
This is the wrong model outputs without using parallel processing:
Even that I set a random seed for NumPy, the outputs using parallel processing still vary for different runs.
ahmedfgad commentedon Mar 1, 2023
Thanks to https://stackoverflow.com/a/75606666/5426539.
Keras is not thread-safe. The issue can be simply solved by cloning the model before calling the
predict()method.The next release will solve the issue.
PyGAD 3.0.0 Release
PyGAD 3.0.0 parallel processing
PyGAD 3.0.0 parallel processing