New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Early stropping (ftol) if cost isn't reduced #397
Comments
It seems that a new argument, ftol_iter, has been added to GlobalBestPSO. It is also suffer from the swarm.best_cost == best_cost_yet_found |
Hi @msat59 thanks for reporting this. The addition of Perhaps the default value must be updated? What do you think @msat59 ? |
This ftol_iter does not fix the issue and I believe that we don't need it. By applying the fix I mentioned, there is no need to use ftol_iter. For example, if I set ftol=1e-10 (with my fix) and also use ftol_iter=5, the cost may go somewhere around 1e-15. This doesn't make sense to me. On the other hand, without my fix, the search may stop when cost is 1e-3 because 5 consecutive costs were equal (without any improvement). To summarize, when the bestcost is below ftol, I think we don't need to have ftol_iter consecutive iteration with bestcost < ftol. |
I should also mention that ftol_inter increases the chance of stopping in a local minima and miss the global one. |
Ok this makes sense, would you like to submit a PR so that we can check? |
Using the version with or without ftol_inter? An option should be added to disable ftol_inter if we are going to keep it. I can do it BTW. |
Good idea on adding a trigger to disable ftol_iter. How about we default that value to |
@msat59 If |
@nishnash54, I added a switch named According to the code, the MATLAB does not use evolutionary or metaheuristic methods to solve the problems so if I am correct, there isn't any occasion that the minimum cost doesn't change in two steps except a local/global minimum is found. In addition, if the current position passes the minima, the algorithm brings it back close to where it was. In metaheuristic methods, several candidates try to find the minimum cost. As we find the minimum cost over all candidates, it is possible that one is stuck in a position and the others cannot find a better solution. So early stopping leads to an immature solution. As a result, I believe that we should add this comparison, Let me test it more, then I implement the fix. I need to test more to see what the best combination of ftol and ftol_iter is. @ljvmiranda921 |
MATLAB documentation has explained everything quite well. Everything is based on a tolerance, not the iteration. |
I agree with you; but it doesn't work for metaheuristic algorithms, I think. Just test sphere() with 5 particles and ftol_iter = 5.
It doesn't converge. So what's the point of using early stopping? To make the algorithm work, I think it's because of the nature of PSO. We don't deal with the current cost of a particle. We deal with the minimum found, so it's possible that a particle moves around the point previously found; but doesn't stay around it for certain iterations. The X is the position of a particle; not the value found by newton's method or so. It passes the minimum, comes back, passes again, and this repeats. For instance for one particle
As you see, the particle will approach to minima, but early stopping stops at a very high cost. |
Hi @nishnash54 , thanks for clarifying and the prompt reply. I apologize that I got confused as well, it seems that we're talking about different use-cases that's why we're thinking of different tolerance strategies. My bad.
I'm a bit hesitant to add As for the fix and API usageActivating both Let me know what you think @msat59 and @nishnash54 ? |
I will comment only on ftol_iter part. |
I agree with all of you; but early stopping is not working for particle swarm optimization. You may stop at second iteration. It's not a local minima, it's somewhere unknown. You will have nothing as a feasible solution. :) Anyway, I don't want to force anyone to accept it. The main problem that I wanted to address is in the optimizer code, with and without ftol_iter. When the best_cost stays constant at two consecutive iteration, regardless of the value of ftol_iter, ### IT IS A COMPLETELY INCORRECT BEHAVIOR. |
I will submit the solution, works with the current form of ftol_iter. @nishnash54, if you don't mind, I am going to rename |
The default value of It seems that the current implementation of Test case: with ftol_iter: Without ftol_iter with n_paricle=10: So imagine it is a SVM calculation. |
I agree that there must be a threshold parameter so that the optimizer can start counting non-improving global best values. If the user knows his problem and has a good estimate of what the objective function value should be, or the least acceptable value, then he/she should be able to put this knowledge into the optimizer. And the cost of implementation and computation is extremely low. |
To fix the constant The approach is to use a small weight if the best_cost didn't changed. As @hakan said and I mentioned earlier, the best practice is to have The only library I have seen so far with early stopping is Keras, which I didn't like its performance; but it has a |
I understand the use case you are referring to @msat59 According to my understanding of the current implementation of
Let me know you thoughts! I would be happy to help on the testing and development of the new implementation. 😃 |
I can also share my global_best.py before opening a PR, if you want to test. |
A note, |
Okay. So, I guess |
I uploaded a test case for various combination of ftol (modified), ftol_iter, and fmin. You need to copy |
Let's park
In short, let's do 1 first, then go with 2 next. cc: @whzup for context |
The progress bar implementation had a small bug when we broke the loop; but I fixed it. I want to add stopping reason to the logger. Please correct the reasons if you think it's not clear: for max iteration: Maximum iterations reached The output of the logger will be: The Let me know your suggesstions @ljvmiranda921 , @nishnash54 . May be the reached at the end of the reason is extra. |
^Good idea and I think that's clear! I suggest that you show what the value was as well:
I know it's small and a bit redundant, but can help the user too! @msat59 |
I've fixed both the early stop and the progress bar bro |
@hakan-gecili This behavior is weird, there is a test that makes sure the required number of iterations ( |
Sorry @nishnash54, it is my bad. I think I was using an earlier commit. Anyway I spent some time of create a test problem. Here are details, it might be useful for somebody. So I am posting details: First I created a 0-1 knapsack problem to test pyswarms PSO (code is given below) Then I cloned the and also I installed stable Next I tested the knapsack problem on both docker image (with Summary: Early stopping does work as it should :) The Knapsack test code: """The following 0-1 Knapsack Problem (KP) is created by
Hakan Gecili for testing purposes. For KP problems please refer to:
Kellerer H, Pferschy U, Pisinger D. Knapsack problems. Berlin: Springer; 2004.
Short definition of the problem: From a set of items, select a subset of items
without violating the knapsack capacity so that the total revenue is maximized.
"""
from numpy import array, round, argwhere
from random import randint, seed
import pyswarms as ps
global number_of_items
number_of_items = 10
global item_range
item_range = range(number_of_items)
# The weight capacity of the knapsack
global capacity
capacity = 50
def get_particle_obj(X, **kwargs):
""" Calculates the objective function value which is
total revenue minus penalty of capacity violations"""
# X is the decision variable. X is vector in the lenght of number of items
# $ value of items
value = kwargs['value']
# weight of items
weight = kwargs['weight']
# Total revenue
revenue = sum([value[i]*round(X[i]) for i in item_range])
# Total weight of selected items
used_capacity = sum([kwargs['weight'][i]*round(X[i]) for i in item_range])
# Total capacity violation with 100 as a penalty cofficient
capacity_violation = 100 * min(0,capacity - used_capacity)
# the objective function minimizes the negative revenue, which is the same
# as maximizing the positive revenue
return -1*(revenue + capacity_violation)
def objective_function(X, **kwargs):
n_particles_ = X.shape[0]
dist = [get_particle_obj(X[i], **kwargs) for i in range(n_particles_)]
return array(dist)
if __name__ == '__main__':
seed(0)
# PARAMETERS
value = [randint(1,number_of_items) for i in item_range]
weight = [randint(1,number_of_items) for i in item_range]
# PSO PARAMETERS
n_particles = 2
n_processes = 2
iterations = 1000
options = {'c1': 2, 'c2': 2, 'w': 0.7}
dim = number_of_items
LB = [0] * dim
UB = [1] * dim
constraints = (array(LB), array(UB))
kwargs = {'value':value,
'weight': weight,
'capacity': capacity
}
KP_optimizer = ps.single.GlobalBestPSO(n_particles=n_particles,
dimensions=dim,
options=options,
bounds=constraints,
bh_strategy='periodic',
ftol = 0.01,
ftol_iter = 50,
velocity_clamp = (-0.5,0.5),
vh_strategy = 'invert')
best_cost, best_pos = KP_optimizer.optimize(objective_function,
iters=iterations,
n_processes= n_processes,
**kwargs)
print("\nThe total knapsack revenue is:\t "+str(-best_cost))
print("\nIndices of selected items:\t " + str(argwhere(round(best_pos)).flatten()))
# The best answer I have found so far is 58$ and selected items are [0 1 2 3 4 5 6 8 9] I set the PSO did not stop before the 50th iteration, which is what we want to see 👍 |
@hakan-gecili Thanks for taking out your time to test this and also for sharing your code and results. I was wondering if I could use this code as a better and improved test case. I could simply copy the code or you can just dump it as a PR to my fork |
Hi @nishnash54 I was busy with my thesis, I just finished reading the PR #401 . You guys were having so much fun :) Yes, You can use my code, actually I have should I posted my previous comment in that PR. |
@ljvmiranda921 , I have more ideas to improve this library. let me know if I can be a contributor at this level of support. Thanks |
Hi @hakan-gecili , thanks for volunteering! If this is related to this Issue, I think giving your thoughts on #401 will be greatly appreciated. If this is on a different matter, then I recommend opening up an Issue and we can put this in our feature backlog! 🙇♂️ |
Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward? This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
Still relevant, stalebot! |
Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward? This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
Describe the bug
When the ftol is used and the best cost is equal in two consecutive iteration, the search is stopped even if the cost is high.
To Reproduce
options = {'c1': 0.5, 'c2': 0.3, 'w':0.9} optimizer = ps.single.GlobalBestPSO(n_particles=100, dimensions=2, options=options, ftol=1e-10) cost, pos = optimizer.optimize(fx.sphere, iters=1000, n_processes=None, verbose=True)
Output is something like this.
Position: [0.06410654 0.02934344], Cost: 0.0049706854704889645
As can be seen, x1 and x2 are far from zero, the cost is relatively high, and it's much higher than 1e-10. The reason is that the algorithm couldn't find lower cost so the swarm.best_cost is equal to best_cost_yet_found. So the search is stopped even if the cost remains high.
Environment (please complete the following information):
How to fix
In optimizer code, an additional check should be performed to ignore if swarm.best_cost == best_cost_yet_found. After applying the fix, the output will be:
Position: [-8.05105641e-06 -6.63608458e-07], Cost: 6.525988549305629e-11
The text was updated successfully, but these errors were encountered: