/*
Optimizer.cpp
Written by Matthew Fisher
Generic non-linear optimizer. Uses conjugate gradient descent (or other inferior/sub-component algorithms like steepest descent.)
For a full description of the Optimizer class, se Optimizer.h.
*/
const double SmallDelta = 1e-4; //small delta used to calculate the derivative
const double LineSearchEpsilon = 1e-4; //starting epsilon for the line search
const int LineSearchIterations = 20; //maximum number of line search iterations
void Optimizer::ComputeGradient()
{
double BaseError = Error(); //store the starting error
double VStart,NewError;
for(UINT i=0;i<Variables.Length();i++)
{
VStart = *(Variables[i].Target); //store the starting weight
*(Variables[i].Target) += SmallDelta; //displace a small amount
NewError = Error(); //get the new error
(*Variables[i].Target) = VStart; //restore the old weight
Variables[i].Partial = (NewError - BaseError) / SmallDelta; //approximate the partial derivative
}
}
void Optimizer::AdaptiveLearning(double &LearningRate, double Alpha, double Beta)
{
double BaseError = Error(); //store the old error
ComputeGradient();
for(UINT i=0;i<Variables.Length();i++)
{
*(Variables[i].Target) -= Variables[i].Partial * LearningRate; //gradient descent
}
double CurError = Error();
if(CurError < BaseError) //if the step was good,
{
LearningRate *= Alpha; //increase the learning rate
}
else
{
for(UINT i=0;i<Variables.Length();i++)
{
*(Variables[i].Target) += Variables[i].Partial * LearningRate; //otherwise reset,
}
LearningRate *= Beta; //and decresase the learning rate
}
}
void Optimizer::MomentumGradientDescent(double LearningRate, double MomentumRate)
{
ComputeGradient();
for(UINT i=0;i<Variables.Length();i++)
{
*(Variables[i].Target) += -Variables[i].Partial * LearningRate + MomentumRate * Variables[i].OldPartial; //proceed according to the previous weight change and the gradient,
Variables[i].OldPartial = -Variables[i].Partial * LearningRate + MomentumRate * Variables[i].OldPartial; //and store this weight change as the previous weight change
}
}
void Optimizer::GradientDescent(double LearningRate)
{
ComputeGradient();
for(UINT i=0;i<Variables.Length();i++)
{
*(Variables[i].Target) -= Variables[i].Partial * LearningRate; //descent along the gradient
}
}
void Optimizer::SteepestDescent()
{
ComputeGradient();
for(UINT i=0;i<Variables.Length();i++)
{
Variables[i].LineSearchDirection = -Variables[i].Partial; //set up the line search direction as the gradient
}
LineSearch(); //search in this direction
}
void Optimizer::LineSearch()
{
bool Exit = false;
double PrevError = Error(), CurError, //errors
DistPrevW, DistCurW, DistNextW, DistM1, DistM2, //five points for line search
ErrorM, ErrorM1, ErrorM2; //current error at the three middle points
for(UINT i=0;i<Variables.Length();i++)
{
Variables[i].LineSearchStart = *(Variables[i].Target); //store where we started
}
UINT LSIters = 0;
while(!Exit)
{
DistPrevW = LineSearchEpsilon * pow(2,LSIters-2);
DistCurW = LineSearchEpsilon * pow(2,LSIters-1);
DistNextW = LineSearchEpsilon * pow(2,LSIters); //calculate the three distances
for(UINT i=0;i<Variables.Length();i++)
{
*(Variables[i].Target) = Variables[i].LineSearchStart + DistNextW * Variables[i].LineSearchDirection; //go the appropriate distance along this direction,
}
CurError = Error(); //get the error
if(CurError > PrevError) Exit = true; //if we gained error, exit the search
else PrevError = CurError; //otherwise keep going
LSIters++;
if(LSIters > 25) //if we're stuck in an infinite loop,
{
cout << "Iterations failed." << endl;
for(UINT i=0;i<Variables.Length();i++)
{
*(Variables[i].Target) = Variables[i].LineSearchStart; //reset
}
return; //and abort
}
}
for(UINT Iterations=0;Iterations<LineSearchIterations;Iterations++) //loop a bunch of times
{
DistM1 = (DistPrevW + DistCurW) * 0.5f;
DistM2 = (DistCurW + DistNextW) * 0.5f; //get the midpoints
for(i=0;i<Variables.Length();i++)
*(Variables[i].Target) = Variables[i].LineSearchStart + DistM1 * Variables[i].LineSearchDirection;
ErrorM1 = Error(); //get the error at the left midpoint
for(i=0;i<Variables.Length();i++)
*(Variables[i].Target) = Variables[i].LineSearchStart + DistM2 * Variables[i].LineSearchDirection;
ErrorM2 = Error(); //get the error at the right midpoint
for(i=0;i<Variables.Length();i++)
*(Variables[i].Target) = Variables[i].LineSearchStart + DistCurW * Variables[i].LineSearchDirection;
ErrorM = Error(); //get the error at the central point
if(ErrorM1 < ErrorM2 && ErrorM1 < ErrorM) { //depending on which is smaller,
DistPrevW = DistPrevW;
DistCurW = DistM1;
DistNextW = DistCurW; //load left three points
} else if(ErrorM < ErrorM2) {
DistPrevW = DistM1;
DistCurW = DistCurW;
DistNextW = DistM2; //load middle three points
} else {
DistPrevW = DistCurW;
DistCurW = DistM2;
DistNextW = DistNextW; //load right three points
}
}
for(i=0;i<Variables.Length();i++) //move into the midpoint
*(Variables[i].Target) = Variables[i].LineSearchStart + DistCurW * Variables[i].LineSearchDirection;
}
void Optimizer::ConjugateReset(int _RepeatRate)
{
for(UINT i=0;i<Variables.Length();i++)
{
Variables[i].OldPartial = 0.0; //reset beta
}
RepeatRate = _RepeatRate; //load the repeat rate
RepeatsLeft = RepeatRate;
}
void Optimizer::ConjugateGradient()
{
double Beta, Numerator = 0.0, Denominator = 0.0;
ComputeGradient();
for(UINT i=0;i<Variables.Length();i++)
{
Numerator += Variables[i].Partial * (Variables[i].Partial - Variables[i].OldPartial);
Denominator += Variables[i].OldPartial * Variables[i].OldPartial; //load the numerator and denominator of beta
}
if(Denominator == 0.0) Beta = 0.0;
else Beta = Numerator / Denominator; //load beta
RepeatsLeft--;
if(RepeatsLeft == 0)
{
RepeatsLeft = RepeatRate; //reset counter has come
Beta = 0.0;
}
for(i=0;i<Variables.Length();i++)
{
Variables[i].LineSearchDirection = -Variables[i].Partial + Beta * Variables[i].LineSearchDirection; //load the line search direction based upon conj. gradient
}
LineSearch(); //search
for(UINT i=0;i<Variables.Length();i++)
{
Variables[i].OldPartial = Variables[i].Partial; //save the current partials
}
}
Top