-
Notifications
You must be signed in to change notification settings - Fork 124
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
Fix for CMA-ES inconsistencies. #193
Conversation
Hi, @zoq kindly have a look at this whenever you get a chance. Thanks. |
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
You can use |
Does it still qualify as breaking reverse compatibility though? Since you made |
Oh! okay, thanks for the clarification @favre49 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, can you update the HISTORY.md file as well.
Hi, @zoq I have made the relevant changes in the code, |
// Boundary transformation shift into feasible pre-image. | ||
if (y < xlow) | ||
{ | ||
y += (ElemType)(r * (1 + (xlow - y) / r)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be y += (ElemType)(r * (1 + (int)(xlow - y) / r));
also is the cast to ElemType
needed, not sure I can see the reason to do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @zoq sorry for replying so late. I did it to ensure that every element of the matrix are of same types if not required I'll remove it. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you don't mind let's remove the extra cast.
You can use assert, here is an example:
|
@zoq, I have made the relevant changes kindly have a look at it whenever you get a chance. Thanks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before we merge, I'd also like to test how robust the changed algorithm is w.r.t the tests.
initialSigma(initialSigma) | ||
{ | ||
assert(this->lowerBound != this->upperBound && "The values of " | ||
"lowerbound and upperbound must be different."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either camelcase the variable names or say lower bound
.
@@ -50,7 +50,7 @@ TEST_CASE("CMAESLogisticRegressionTest", "[CMAESTest]") | |||
responses, testResponses, shuffledResponses); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add a test that makes sure CMAES keeps the answers within the given bounds?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @favre49 sorry for being late on this. Can you please elaborate on the test for this should be written? Should I consider an armadillo matrix which is out of bounds and then apply the transformation to see if the values are within the specified range? Sorry, that I didn't get the gist of what you were saying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant we should add a test that ensures the optimized coordinates are within the given bounds [-20,20]. Something like REQUIRE(coordinates[i] <=20 && coordinates[i] >= -20)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @favre49 the test has been added kindly have a look whenever you get a chance. Thanks.
Co-authored-by: favre49 <40389657+favre49@users.noreply.github.com>
Hello, @zoq kindly have a look at this whenever you get a chance. I have made all the necessary changes required. Thanks. |
const double diff = (upperBound - lowerBound) / 2.0; | ||
const double al = std::min(diff, (1 + std::abs(lowerBound)) / 20.0); | ||
const double au = std::min(diff, (1 + std::abs(upperBound)) / 20.0); | ||
const double xlow = lowerBound - 2 * al - diff; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think we should make t possible to define bounds for all coordinates similar to what has been done for the PSO method:
ensmallen/include/ensmallen_bits/pso/pso.hpp
Lines 91 to 92 in 70a2be8
const arma::mat& lowerBound = arma::ones(1, 1), | |
const arma::mat& upperBound = arma::ones(1, 1), |
It uses a 1x1 matrix as a default, so the current implementation but you use a matrix of the same size as the coordinates as well. Let me know what you think. We can also implement the change another time and merge this as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @zoq, I implemented this boundary handing function after referring the N Hansen's implementation and in my opinion, I think we should merge this as it is and if there are any issues in the future I will try to change this accordingly. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, fine for me.
Hi, @zoq Are there any more changes required for this? |
No I think this is fine, I just have to double check the windows issue, but for some reason I can't access the page right now. |
Looks like the decomposition failed on the windows build:
|
This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions! 👍 |
Hi, @zoq please keep this open I want to work on it whenever I get a chance. Thanks |
@mlpack-jenkins test this please |
tests/cmaes_test.cpp
Outdated
*/ | ||
TEST_CASE("BoundaryConditionTestFunction", "[CMAESTest]") | ||
{ | ||
SGDTestFunction f; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mind to change the object function here, to something more complex, the SGD function has only 3 parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gaurav-singh1998 let me know if you need any help with this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @zoq, sorry for being so late on this(life got messed up). I want to take this up again. Can you provide some help as to what objective functions I need to use and what other changes do I need to make? Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gaurav-singh1998 No worries I hope everything is going well. Let's test this one on the LogisticRegression
function, here is an example -
ensmallen/tests/cmaes_test.cpp
Lines 40 to 66 in 60a99f7
TEST_CASE("CMAESLogisticRegressionTest", "[CMAESTest]") | |
{ | |
const size_t trials = 3; | |
bool success = false; | |
for (size_t trial = 0; trial < trials; ++trial) | |
{ | |
arma::mat data, testData, shuffledData; | |
arma::Row<size_t> responses, testResponses, shuffledResponses; | |
LogisticRegressionTestData(data, testData, shuffledData, | |
responses, testResponses, shuffledResponses); | |
LogisticRegression<> lr(shuffledData, shuffledResponses, 0.5); | |
CMAES<> cmaes(0, -1, 1, 32, 200, 1e-3); | |
arma::mat coordinates = lr.GetInitialPoint(); | |
cmaes.Optimize(lr, coordinates); | |
// Ensure that the error is close to zero. | |
const double acc = lr.ComputeAccuracy(data, responses, coordinates); | |
const double testAcc = lr.ComputeAccuracy(testData, testResponses, | |
coordinates); | |
if (acc >= 99.7 && testAcc >= 99.4) | |
{ | |
success = true; | |
break; | |
} | |
} |
Hi, @zoq made the relevant changes in the test function. Kindly have a look at it now. |
BaseMatType initialVal; | ||
initialVal.randu(iterate.n_rows, iterate.n_cols); | ||
initialVal += (BaseMatType)(iterateIn); | ||
mPosition[0] = initialVal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's still valid to apply the lower/upper bound here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the slow response, I'll review the code over the weekend. |
// Transforms the candidate into the given bounds. | ||
template<typename SelectionPolicyType> | ||
template<typename BaseMatType> | ||
void CMAES<SelectionPolicyType>::BoundaryTransform(BaseMatType& matrix) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I dived into the code and I think it makes sense to introduce another policy e.g. TransformationPolicy
so we can enable/disable the boundary constraint. Let me know what you think, I can push the changes to you branch, if you like?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @zoq thanks for the review. I will try and implement the TransformationPolicy
this week. Can you point to some source code to which I can refer before implementing this? Thank you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So basically it's similar to how the update policies are implemented https://github.com/mlpack/ensmallen/tree/master/include/ensmallen_bits/sgd/update_policies.
We basically add a new template type called TransformationPolicy
or something like that and define an interface. I think we just need two functions at this point Transform
and Inverse
. So the class policy class for the BoundaryBoxConstraint
could look like:
class BoundaryBoxConstraint
{
public:
void Transform(...)
{
}
void Inverse(...)
{
}
}
we also a policy class that does nothing:
class EmptyTransformation
{
public:
void Transform(...)
{
}
void Inverse(...)
{
}
}
in the CMAES
class we can than use an instantiation of the TransformationPolicy
and call the Transform
and Inverse
methods. Hopefully what I wrote above makes sense, happy to clarify anything.
This pull request aims to resolve inconsistencies that currently exist in CMA-ES implementation. I have added the boundary constraints as per this code snippet which is a part of CMA-ES implementation in C by N. Hansen. I will make the appropriate changes in documentation also after a member's approval of every change I made. Thank you.