00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
#include "ConjGradientOptimizer.h"
00044
#include <plearn/math/TMat_maths_impl.h>
00045
00046
namespace PLearn {
00047
using namespace std;
00048
00049
00050
00051
00052 ConjGradientOptimizer::ConjGradientOptimizer(
00053
real the_starting_step_size,
00054
real the_restart_coeff,
00055
real the_epsilon,
00056
real the_sigma,
00057
real the_rho,
00058
real the_fmax,
00059
real the_stop_epsilon,
00060
real the_tau1,
00061
real the_tau2,
00062
real the_tau3,
00063
int n_updates,
const string& filename,
00064
int every_iterations)
00065 :
inherited(n_updates, filename, every_iterations),
00066 compute_cost(1),
00067 line_search_algo(1),
00068 find_new_direction_formula(1),
00069 starting_step_size(the_starting_step_size), restart_coeff(the_restart_coeff),
00070 epsilon(the_epsilon),
00071 sigma(the_sigma), rho(the_rho), fmax(the_fmax),
00072 stop_epsilon(the_stop_epsilon), tau1(the_tau1), tau2(the_tau2),
00073 tau3(the_tau3), max_steps(5), initial_step(0.01), low_enough(1e-6) {}
00074
00075 ConjGradientOptimizer::ConjGradientOptimizer(
00076
VarArray the_params,
00077
Var the_cost,
00078
real the_starting_step_size,
00079
real the_restart_coeff,
00080
real the_epsilon,
00081
real the_sigma,
00082
real the_rho,
00083
real the_fmax,
00084
real the_stop_epsilon,
00085
real the_tau1,
00086
real the_tau2,
00087
real the_tau3,
00088
int n_updates,
const string& filename,
00089
int every_iterations)
00090 :
inherited(the_params, the_cost, n_updates, filename, every_iterations),
00091 starting_step_size(the_starting_step_size), restart_coeff(the_restart_coeff),
00092 epsilon(the_epsilon),
00093 sigma(the_sigma), rho(the_rho), fmax(the_fmax),
00094 stop_epsilon(the_stop_epsilon), tau1(the_tau1), tau2(the_tau2),
00095 tau3(the_tau3) {
00096 cout <<
"Warning: you should use the constructor ConjGradientOptimizer(), or some default options may not be set properly" <<
endl;
00097 }
00098
00099 ConjGradientOptimizer::ConjGradientOptimizer(
00100
VarArray the_params,
00101
Var the_cost,
00102
VarArray the_update_for_measure,
00103
real the_starting_step_size,
00104
real the_restart_coeff,
00105
real the_epsilon,
00106
real the_sigma,
00107
real the_rho,
00108
real the_fmax,
00109
real the_stop_epsilon,
00110
real the_tau1,
00111
real the_tau2,
00112
real the_tau3,
00113
int n_updates,
const string& filename,
00114
int every_iterations)
00115 :
inherited(the_params, the_cost, the_update_for_measure,
00116 n_updates, filename, every_iterations),
00117 starting_step_size(the_starting_step_size), restart_coeff(the_restart_coeff),
00118 epsilon(the_epsilon),
00119 sigma(the_sigma), rho(the_rho), fmax(the_fmax),
00120 stop_epsilon(the_stop_epsilon), tau1(the_tau1), tau2(the_tau2),
00121 tau3(the_tau3) {
00122 cout <<
"Warning: you should use the constructor ConjGradientOptimizer(), or some default options may not be set properly" <<
endl;
00123 }
00124
00125
PLEARN_IMPLEMENT_OBJECT(
ConjGradientOptimizer,
00126
"Optimizer based on the conjugate gradient method.",
00127
"The conjugate gradient algorithm is basically the following :\n"
00128
"- 0: initialize the search direction d = -gradient\n"
00129
"- 1: perform a line search along direction d for the minimum of the gradient\n"
00130
"- 2: move to this minimum, update the search direction d and go to step 1\n"
00131
"There are various methods available through the options for both steps 1 and 2."
00132 );
00133
00134
00135
00136
00137 void ConjGradientOptimizer::declareOptions(
OptionList& ol)
00138 {
00139
declareOption(ol,
"starting_step_size", &ConjGradientOptimizer::starting_step_size, OptionBase::buildoption,
00140
" The initial step size for the line search algorithm.\n \
00141
This option has very little influence on the algorithm performance, \n \
00142
since it is only used for the first iteration, and is set by the algorithm\n \
00143
in the later stages.\n");
00144
00145
declareOption(ol,
"restart_coeff", &ConjGradientOptimizer::restart_coeff, OptionBase::buildoption,
00146
" The restart coefficient. A restart is triggered when the following condition is met :\n \
00147
abs(gradient(k-1).gradient(k)) >= restart_coeff * gradient(k)^2\n \
00148
If no restart is wanted, any high value (e.g. 100) will prevent it.\n");
00149
00150
declareOption(ol,
"line_search_algo", &ConjGradientOptimizer::line_search_algo, OptionBase::buildoption,
00151
" The kind of line search algorithm used :\n \
00152
1. The line search algorithm described in :\n \
00153
""Practical Methods of Optimization, 2nd Ed""\n \
00154
by Fletcher (1987).\n \
00155
This algorithm seeks a minimum m of f(x)=C(x0+x.d) satisfying the two constraints :\n \
00156
i. abs(f'(m)) < -sigma.f'(0)\n \
00157
ii. f(m) < f(0) + m.rho.f'(0)\n \
00158
with rho < sigma <= 1.\n \
00159
2. The GSearch algorithm, described in :\n \
00160
""Direct Gradient-Based Reinforcement Learning: II. Gradient Ascent Algorithms and Experiments""\n \
00161
by J.Baxter, L. Weaver, P. Bartlett (1999).\n \
00162
3. A Newton line search algorithm for quadratic costs only.\n");
00163
00164
declareOption(ol,
"find_new_direction_formula", &ConjGradientOptimizer::find_new_direction_formula, OptionBase::buildoption,
00165
" The kind of formula used in step 2 of the conjugate gradient algorithm to find the\n \
00166
new search direction :\n \
00167
1. ConjPOMPD : the formula associated with GSearch in the same paper. It is\n \
00168
almost the same as the Polak-Ribiere formula.\n \
00169
2. Dai - Yuan\n \
00170
3. Fletcher - Reeves\n \
00171
4. Hestenes - Stiefel\n \
00172
5. Polak - Ribiere : this is probably the most commonly used.\n \
00173
The value of this option (from 1 to 5) indicates the formula used.\n");
00174
00175
declareOption(ol,
"epsilon", &ConjGradientOptimizer::epsilon, OptionBase::buildoption,
00176
" GSearch specific option : the gradient resolution.\n \
00177
This small value is used in the GSearch algorithm instead of 0, to provide\n \
00178
some robustness against errors in the estimate of the gradient, and to\n \
00179
prevent the algorithm from looping indefinitely if there is no local minimum.\n \
00180
Any small value should work, and the overall performance should not depend\n \
00181
much on it.\n ");
00182
00183
00184
declareOption(ol,
"sigma", &ConjGradientOptimizer::sigma, OptionBase::buildoption,
00185
" Fletcher's line search specific option : constraint parameter in i.\n");
00186
00187
declareOption(ol,
"rho", &ConjGradientOptimizer::rho, OptionBase::buildoption,
00188
" Fletcher's line search specific option : constraint parameter in ii.\n");
00189
00190
declareOption(ol,
"fmax", &ConjGradientOptimizer::fmax, OptionBase::buildoption,
00191
" Fletcher's line search specific option : good enough minimum.\n \
00192
If it finds a point n such that f(n) < fmax, then the line search returns n as minimum\n\
00193
As a consequence, DO NOT USE 0 if f can be negative\n");
00194
00195
declareOption(ol,
"stop_epsilon", &ConjGradientOptimizer::stop_epsilon, OptionBase::buildoption,
00196
" Fletcher's line search specific option : stopping criterium.\n \
00197
This option allows the algorithm to detect when no improvement is possible along\n \
00198
the search direction. It should be set to a small value, or we may stop too early.\n \
00199
IMPORTANT: this same option is also used to compute the next step size. If you get\n \
00200
a nan in the cost, try a higher stop_epsilon.\n");
00201
00202
declareOption(ol,
"tau1", &ConjGradientOptimizer::tau1, OptionBase::buildoption,
00203
" Fletcher's line search specific option : bracketing parameter.\n \
00204
This option controls how fast is augmenting the bracketing interval in the first\n \
00205
phase of the line search algorithm.\n \
00206
Fletcher reports good empirical results with tau1 = 9.\n");
00207
00208
declareOption(ol,
"tau2", &ConjGradientOptimizer::tau2, OptionBase::buildoption,
00209
" Fletcher's line search specific option : bracketing parameter.\n \
00210
This option controls how fast is augmenting the left side of the bracketing\n \
00211
interval in the second phase of the line search algorithm.\n \
00212
Fletcher reports good empirical results with tau2 = 0.1\n");
00213
00214
declareOption(ol,
"tau3", &ConjGradientOptimizer::tau3, OptionBase::buildoption,
00215
" Fletcher's line search specific option : bracketing parameter.\n \
00216
This option controls how fast is decreasing the right side of the bracketing\n \
00217
interval in the second phase of the line search algorithm.\n \
00218
Fletcher reports good empirical results with tau3 = 0.5\n");
00219
00220
declareOption(ol,
"max_steps", &ConjGradientOptimizer::max_steps, OptionBase::buildoption,
00221
" Newton line search specific option : maximum number of steps at each iteration.\n \
00222
This option defines how many steps will be performed to find the minimum, if no\n \
00223
value < low_enough is found for the gradient (this can happen if the cost isn't\n \
00224
perfectly quadratic).\n");
00225
00226
declareOption(ol,
"initial_step", &ConjGradientOptimizer::initial_step, OptionBase::buildoption,
00227
" Newton line search specific option : value of the first step.\n \
00228
This options controls the size of the first step made in the search direction.\n");
00229
00230
declareOption(ol,
"low_enough", &ConjGradientOptimizer::low_enough, OptionBase::buildoption,
00231
" Newton line search specific option : stopping criterion for the gradient.\n \
00232
We say the minimum has been found if we have abs(gradient) < low_enough.\n");
00233
00234
declareOption(ol,
"compute_cost", &ConjGradientOptimizer::compute_cost, OptionBase::buildoption,
00235
" If set to 1, will compute and display the mean cost at each epoch.\n");
00236
00237 inherited::declareOptions(ol);
00238 }
00239
00240
00241
00242
00243
00244
00246
00248 void ConjGradientOptimizer::build_() {
00249
00250
int n = params.
nelems();
00251
if (n > 0) {
00252
current_opp_gradient.
resize(n);
00253
search_direction.
resize(n);
00254
tmp_storage.
resize(n);
00255
delta.
resize(n);
00256
if (cost.
length() > 0) {
00257
meancost.
resize(cost->size());
00258 }
00259 }
00260 }
00261
00263
00265 void ConjGradientOptimizer::computeCostAndDerivative(
00266
real alpha,
ConjGradientOptimizer* opt,
real& cost,
real& derivative) {
00267
if (alpha == 0) {
00268 cost = opt->
last_cost;
00269 derivative = -
dot(opt->
search_direction, opt->
current_opp_gradient);
00270 }
else {
00271
00272 opt->
params.
copyTo(opt->
tmp_storage);
00273 opt->
params.
update(alpha, opt->
search_direction);
00274 opt->
proppath.
clearGradient();
00275 opt->
params.
clearGradient();
00276 opt->
cost->gradient[0] = 1;
00277 opt->
proppath.
fbprop();
00278 opt->
params.
copyGradientTo(opt->
delta);
00279 cost = opt->
cost->value[0];
00280 derivative =
dot(opt->
search_direction, opt->
delta);
00281 opt->
params.
copyFrom(opt->
tmp_storage);
00282 }
00283 }
00284
00286
00288 real ConjGradientOptimizer::computeCostValue(
00289
real alpha,
00290
ConjGradientOptimizer* opt) {
00291
if (alpha == 0) {
00292
return opt->
last_cost;
00293 }
00294 opt->
params.
copyTo(opt->
tmp_storage);
00295 opt->
params.
update(alpha, opt->
search_direction);
00296 opt->
proppath.
fprop();
00297
real c = opt->
cost->value[0];
00298 opt->
params.
copyFrom(opt->
tmp_storage);
00299
return c;
00300 }
00301
00303
00305 real ConjGradientOptimizer::computeDerivative(
00306
real alpha,
00307
ConjGradientOptimizer* opt) {
00308
if (alpha == 0)
00309
return -
dot(opt->
search_direction, opt->
current_opp_gradient);
00310 opt->
params.
copyTo(opt->
tmp_storage);
00311 opt->
params.
update(alpha, opt->
search_direction);
00312 Optimizer::computeGradient(opt, opt->
delta);
00313 opt->
params.
copyFrom(opt->
tmp_storage);
00314
return dot(opt->
search_direction, opt->
delta);
00315 }
00316
00318
00320 real ConjGradientOptimizer::conjpomdp (
00321
void (*grad)(
Optimizer*,
const Vec& gradient),
00322
ConjGradientOptimizer* opt) {
00323
int i;
00324
00325
real norm_g =
pownorm(opt->
current_opp_gradient);
00326
00327
for (i=0; i<opt->
current_opp_gradient.
length(); i++) {
00328 opt->
tmp_storage[i] = opt->
delta[i]-opt->
current_opp_gradient[i];
00329 }
00330
real gamma =
dot(opt->
tmp_storage, opt->
delta) / norm_g;
00331
00332
for (i=0; i<opt->
search_direction.
length(); i++) {
00333 opt->
tmp_storage[i] = opt->
delta[i] + gamma * opt->
search_direction[i];
00334 }
00335
if (
dot(opt->
tmp_storage, opt->
delta) < 0)
00336
return 0;
00337
else
00338
return gamma;
00339 }
00340
00342
00344 void ConjGradientOptimizer::cubicInterpol(
00345
real f0,
real f1,
real g0,
real g1,
00346
real& a,
real& b,
real& c,
real& d) {
00347 d = f0;
00348 c = g0;
00349 b = 3*(f1-f0) - 2*g0 - g1;
00350 a = g0 + g1 - 2*(f1 - f0);
00351 }
00352
00354
00356 real ConjGradientOptimizer::daiYuan (
00357
void (*grad)(
Optimizer*,
const Vec&),
00358
ConjGradientOptimizer* opt) {
00359
real norm_grad =
pownorm(opt->
delta);
00360
for (
int i=0; i<opt->
current_opp_gradient.
length(); i++) {
00361 opt->
tmp_storage[i] = -opt->
delta[i] + opt->
current_opp_gradient[i];
00362 }
00363
real gamma = norm_grad /
dot(opt->
search_direction, opt->
tmp_storage);
00364
return gamma;
00365 }
00366
00368
00370 bool ConjGradientOptimizer::findDirection() {
00371
bool isFinished =
false;
00372
real gamma;
00373
switch (
find_new_direction_formula) {
00374
case 1:
00375 gamma =
conjpomdp(computeOppositeGradient,
this);
00376
break;
00377
case 2:
00378 gamma =
daiYuan(computeOppositeGradient,
this);
00379
break;
00380
case 3:
00381 gamma =
fletcherReeves(computeOppositeGradient,
this);
00382
break;
00383
case 4:
00384 gamma =
hestenesStiefel(computeOppositeGradient,
this);
00385
break;
00386
case 5:
00387 gamma =
polakRibiere(computeOppositeGradient,
this);
00388
break;
00389
default:
00390 cout <<
"Warning ! Invalid conjugate gradient formula !" <<
endl;
00391 gamma = 0;
00392
break;
00393 }
00394
00395
if (gamma < 0) {
00396 cout <<
"gamma < 0 ! gamma = " << gamma <<
" ==> Restarting" <<
endl;
00397 gamma = 0;
00398 }
00399
else {
00400
real dp =
dot(
delta,
current_opp_gradient);
00401
real delta_n =
pownorm(
delta);
00402
if (
abs(dp) >
restart_coeff *delta_n ) {
00403
00404 gamma = 0;
00405 }
00406 }
00407
updateSearchDirection(gamma);
00408
00409
00410
00411 isFinished =
false;
00412
if (isFinished)
00413 cout <<
"Gradient is small enough, time to stop" <<
endl;
00414
return isFinished;
00415 }
00416
00418
00420 real ConjGradientOptimizer::findMinWithCubicInterpol (
00421
real p1,
00422
real p2,
00423
real mini,
00424
real maxi,
00425
real f0,
00426
real f1,
00427
real g0,
00428
real g1) {
00429
00430
real tmp;
00431
if (p2 < p1) {
00432 tmp = p2;
00433 p2 = p1;
00434 p1 = tmp;
00435 }
00436
if (maxi < mini) {
00437 tmp = maxi;
00438 maxi = mini;
00439 mini = tmp;
00440 }
00441
00442
00443
00444
00445 g0 = g0 * (p2-p1);
00446 g1 = g1 * (p2-p1);
00447
real a, b, c, d;
00448
00449
cubicInterpol(f0, f1, g0, g1, a, b, c, d);
00450
00451
real mini_transformed = mini;
00452
real maxi_transformed = maxi;
00453
if (mini != -FLT_MAX)
00454 mini_transformed = (mini - p1) / (p2 - p1);
00455
if (maxi != FLT_MAX)
00456 maxi_transformed = (maxi - p1) / (p2 - p1);
00457
real xmin =
minCubic(a, b, c, mini_transformed, maxi_transformed);
00458
if (xmin == -FLT_MAX || xmin == FLT_MAX)
00459
return xmin;
00460
00461
return p1 + xmin*(p2-p1);
00462 }
00463
00465
00467 real ConjGradientOptimizer::findMinWithQuadInterpol(
00468
int q,
real sum_x,
real sum_x_2,
real sum_x_3,
real sum_x_4,
00469
real sum_c_x_2,
real sum_g_x,
real sum_c_x,
real sum_c,
real sum_g) {
00470
00471
real q2 = q*q;
00472
real sum_x_2_quad = sum_x_2 * sum_x_2;
00473
real sum_x_quad = sum_x*sum_x;
00474
real sum_x_2_sum_x = sum_x_2*sum_x;
00475
real sum_x_2_sum_x_3 = sum_x_2*sum_x_3;
00476
real sum_x_3_sum_x = sum_x_3*sum_x;
00477
00478
real denom =
00479 (-4*q2*sum_x_2 - 3*q*sum_x_2_quad + sum_x_2_quad*sum_x_2 +
00480 q*sum_x_3*sum_x_3 - q2*sum_x_4 - q*sum_x_2*sum_x_4 + 4*q*sum_x_3_sum_x -
00481 2*sum_x_2_sum_x_3*sum_x + 4*q*sum_x_quad + sum_x_4*sum_x_quad);
00482
00483
real a =
00484 -(q2*sum_c_x_2 + 2*q2*sum_g_x - q*sum_c*sum_x_2 +
00485 q*sum_c_x_2*sum_x_2 + 2*q*sum_g_x*sum_x_2 - sum_c*sum_x_2_quad -
00486 q*sum_c_x*sum_x_3 - q*sum_g*sum_x_3 - 2*q*sum_c_x*sum_x -
00487 2*q*sum_g*sum_x + sum_c_x*sum_x_2_sum_x + sum_g*sum_x_2_sum_x +
00488 sum_c*sum_x_3_sum_x + 2*sum_c*sum_x_quad - sum_c_x_2*sum_x_quad -
00489 2*sum_g_x*sum_x_quad) / denom;
00490
00491
real b =
00492 -(4*q*sum_c_x*sum_x_2 + 4*q*sum_g*sum_x_2 -
00493 sum_c_x*sum_x_2_quad - sum_g*sum_x_2_quad - q*sum_c_x_2*sum_x_3 -
00494 2*q*sum_g_x*sum_x_3 + sum_c*sum_x_2_sum_x_3 + q*sum_c_x*sum_x_4 +
00495 q*sum_g*sum_x_4 - 2*q*sum_c_x_2*sum_x - 4*q*sum_g_x*sum_x -
00496 2*sum_c*sum_x_2_sum_x + sum_c_x_2*sum_x_2_sum_x +
00497 2*sum_g_x*sum_x_2_sum_x - sum_c*sum_x_4*sum_x) / denom;
00498
00499
real xmin = -b / (2*a);
00500
return xmin;
00501
00502 }
00503
00505
00507 real ConjGradientOptimizer::fletcherReeves (
00508
void (*grad)(
Optimizer*,
const Vec&),
00509
ConjGradientOptimizer* opt) {
00510
00511
real gamma =
pownorm(opt->
delta) /
pownorm(opt->
current_opp_gradient);
00512
return gamma;
00513 }
00514
00516
00518 real ConjGradientOptimizer::fletcherSearch (
real mu) {
00519
real alpha =
fletcherSearchMain (
00520
computeCostValue,
00521
computeDerivative,
00522
this,
00523
sigma,
00524
rho,
00525
fmax,
00526
stop_epsilon,
00527
tau1,
00528
tau2,
00529
tau3,
00530
current_step_size,
00531 mu);
00532
return alpha;
00533 }
00534
00536
00538 real ConjGradientOptimizer::fletcherSearchMain (
00539
real (*f)(
real,
ConjGradientOptimizer* opt),
00540 real (*g)(real,
ConjGradientOptimizer* opt),
00541
ConjGradientOptimizer* opt,
00542 real sigma,
00543 real rho,
00544 real fmax,
00545 real epsilon,
00546 real tau1,
00547 real tau2,
00548 real tau3,
00549 real alpha1,
00550 real mu) {
00551
00552
00553 real alpha0 = 0;
00554
00555
00556
00557 real alpha2, f0, f_1=0, f_0, g0, g_1=0, g_0, a1=0, a2, b1=0, b2;
00558 g0 = (*g)(0, opt);
00559 f0 = (*f)(0, opt);
00560 f_0 = f0;
00561 g_0 = g0;
00562
if (mu == FLT_MAX)
00563 mu = (fmax - f0) / (rho * g0);
00564
if (alpha1 == FLT_MAX)
00565 alpha1 = mu / 100;
00566
if (g0 >= 0) {
00567 cout <<
"Warning : df/dx(0) >= 0 !" <<
endl;
00568
return 0;
00569 }
00570
bool isBracketed =
false;
00571 alpha2 = alpha1 + 1;
00572
00573
00574
while (!isBracketed) {
00575
00576
if (alpha1 == mu && alpha1 == alpha2) {
00577 cout <<
"Warning : alpha1 == alpha2 == mu during bracketing" <<
endl;
00578
return alpha1;
00579 }
00580 f_1 = (*f)(alpha1, opt);
00581
if (f_1 <= fmax) {
00582 cout <<
"fmax reached !" <<
endl;
00583 opt->
early_stop =
true;
00584
return alpha1;
00585 }
00586
if (f_1 > f0 + alpha1 * rho * g0 || f_1 > f_0) {
00587
00588 a1 = alpha0;
00589 b1 = alpha1;
00590 isBracketed =
true;
00591
00592 }
else {
00593 g_1 = (*g)(alpha1, opt);
00594
if (
abs(g_1) < -sigma * g0) {
00595
00596
return alpha1;
00597 }
00598
if (g_1 >= 0) {
00599 a1 = alpha1;
00600 b1 = alpha0;
00601 real tmp = f_0;
00602 f_0 = f_1;
00603 f_1 = tmp;
00604 tmp = g_0;
00605 g_0 = g_1;
00606 g_1 = tmp;
00607 isBracketed =
true;
00608 }
else {
00609
if (mu <= 2*alpha1 - alpha0)
00610 alpha2 = mu;
00611
else
00612 alpha2 =
findMinWithCubicInterpol(
00613 alpha1, alpha0,
00614 2*alpha1 - alpha0,
min(mu, alpha1 + tau1 * (alpha1 - alpha0)),
00615 f_1, f_0, g_1, g_0);
00616 }
00617 }
00618
if (!isBracketed) {
00619 alpha0 = alpha1;
00620 alpha1 = alpha2;
00621 f_0 = f_1;
00622 g_0 = g_1;
00623 }
00624 }
00625
00626
00627
00628
00629
00630 real f1,g1;
00631
bool repeated =
false;
00632
while (
true) {
00633
00634
00635 alpha1 =
findMinWithCubicInterpol(
00636 a1, b1,
00637 a1 + tau2 * (b1-a1), b1 - tau3 * (b1-a1),
00638 f_0, f_1, g_0, g_1);
00639 g1 = (*g)(alpha1, opt);
00640 f1 = opt->
cost->value[0];
00641
bool small= ((a1 - alpha1) * g_0 <= epsilon);
00642
if (small && (a1>0 || repeated)) {
00643
00644
return a1;
00645 }
00646
if (small) repeated=
true;
00647
00648
if (f1 > f0 + rho * alpha1 * g0 || f1 >= f_0) {
00649 a2 = a1;
00650 b2 = alpha1;
00651 f_1 = f1;
00652 g_1 = g1;
00653 }
else {
00654
if (
abs(g1) <= -sigma * g0) {
00655
00656
return alpha1;
00657 }
00658
if ((b1 - a1) * g1 >= 0) {
00659 b2 = a1;
00660 f_1 = f_0;
00661 g_1 = g_0;
00662 }
else
00663 b2 = b1;
00664 a2 = alpha1;
00665 f_0 = f1;
00666 g_0 = g1;
00667 }
00668 a1 = a2;
00669 b1 = b2;
00670 }
00671 }
00672
00674
00676 real ConjGradientOptimizer::gSearch (
void (*grad)(
Optimizer*,
const Vec&)) {
00677
00678 real step =
current_step_size;
00679 real sp, sm, pp, pm;
00680
00681
00682 params.
copyTo(
tmp_storage);
00683
00684 params.
update(step,
search_direction);
00685 (*grad)(
this,
delta);
00686 real prod =
dot(
delta,
search_direction);
00687
if (prod < 0) {
00688
00689
do {
00690 sp = step;
00691 pp = prod;
00692 step /= 2.0;
00693 params.
update(-step,
search_direction);
00694 (*grad)(
this,
delta);
00695 prod =
dot(
delta,
search_direction);
00696 }
while (prod < -
epsilon);
00697 sm = step;
00698 pm = prod;
00699 }
00700
else {
00701
00702
do {
00703 sm = step;
00704 pm = prod;
00705 params.
update(step,
search_direction);
00706 (*grad)(
this,
delta);
00707 prod =
dot(
delta,
search_direction);
00708 step *= 2.0;
00709 }
while (prod >
epsilon);
00710 sp = step;
00711 pp = prod;
00712 }
00713
00714
if (pm > 0 && pp < 0)
00715 step = (sm*pp - sp*pm) / (pp - pm);
00716
else
00717 step = (sm+sp) / 2;
00718
00719
00720 params.
copyFrom(
tmp_storage);
00721
return step;
00722 }
00723
00725
00727 real ConjGradientOptimizer::hestenesStiefel (
00728
void (*grad)(
Optimizer*,
const Vec&),
00729
ConjGradientOptimizer* opt) {
00730
int i;
00731
00732
00733
for (i=0; i<opt->
current_opp_gradient.
length(); i++) {
00734 opt->
tmp_storage[i] = opt->
delta[i] - opt->
current_opp_gradient[i];
00735 }
00736 real gamma = -
dot(opt->
delta, opt->
tmp_storage) /
dot(opt->
search_direction, opt->
tmp_storage);
00737
return gamma;
00738 }
00739
00741
00743 bool ConjGradientOptimizer::lineSearch() {
00744 real step;
00745
switch (
line_search_algo) {
00746
case 1:
00747 step =
fletcherSearch();
00748
break;
00749
case 2:
00750 step =
gSearch(computeOppositeGradient);
00751
break;
00752
case 3:
00753 step =
newtonSearch(
max_steps,
initial_step,
low_enough);
00754
break;
00755
default:
00756 cout <<
"Warning ! Invalid conjugate gradient line search algorithm !" <<
endl;
00757 step = 0;
00758
break;
00759 }
00760
if (step < 0)
00761 cout <<
"Ouch, negative step !" <<
endl;
00762
if (step != 0) params.
update(step,
search_direction);
00763
if (step == 0)
00764 cout <<
"No more progress made by the line search, stopping" <<
endl;
00765
return (step == 0);
00766 }
00767
00769
00771 real ConjGradientOptimizer::minCubic(
00772 real a, real b, real c,
00773 real mini, real maxi) {
00774
if (a == 0 || (b != 0 &&
abs(a/b) < 0.0001))
00775
return minQuadratic(b, c, mini, maxi);
00776
00777 real aa = 3*a;
00778 real bb = 2*b;
00779 real d = bb*bb - 4 * aa * c;
00780
if (d <= 0) {
00781
if (a > 0)
00782
return mini;
00783
else
00784
return maxi;
00785 }
else {
00786 d =
sqrt(d);
00787 real p2 = (-bb + d) / (2*aa);
00788
if (a > 0) {
00789
if (p2 < mini || mini == -FLT_MAX)
00790
return mini;
00791
if (p2 > maxi) {
00792
if ( (mini-maxi) * ( ( a * mini + b) * (mini + maxi) + a * maxi * maxi + c) > 0)
00793
00794
00795
return maxi;
00796
else
00797
return mini;
00798 }
00799
if ((maxi-p2) * (( a * maxi + b) * (maxi + p2) + a * p2 * p2 + c) > 0)
00800
00801
00802
return p2;
00803
else
00804
return mini;
00805 }
else {
00806
if (p2 > maxi || maxi == FLT_MAX)
00807
return maxi;
00808
if (p2 < mini) {
00809
if ((mini-maxi) * (( a * mini + b) * (mini + maxi) + a * maxi * maxi + c) > 0)
00810
00811
00812
return maxi;
00813
else
00814
return mini;
00815 }
00816
if ((maxi-p2) * (( a * maxi + b) * (maxi + p2) + a * p2 * p2 + c) > 0)
00817
00818
00819
return p2;
00820
else
00821
return maxi;
00822 }
00823 }
00824 }
00825
00827
00829 real ConjGradientOptimizer::minQuadratic(
00830 real a, real b,
00831 real mini, real maxi) {
00832
if (a == 0 || (b != 0 &&
abs(a/b) < 0.0001)) {
00833
if (b > 0)
00834
return mini;
00835
else
00836
return maxi;
00837 }
00838
if (a < 0) {
00839
if (mini == -FLT_MAX)
00840
return -FLT_MAX;
00841
if (maxi == FLT_MAX)
00842
return FLT_MAX;
00843
if (mini*mini + mini * b / a > maxi*maxi + maxi * b / a)
00844
return mini;
00845
else
00846
return maxi;
00847 }
00848
00849 real the_min = -b / (2*a);
00850
if (the_min < mini)
00851
return mini;
00852
if (the_min > maxi)
00853
return maxi;
00854
return the_min;
00855 }
00856
00858
00860 real ConjGradientOptimizer::newtonSearch(
00861
int max_steps,
00862 real initial_step,
00863 real low_enough
00864 ) {
00865
Vec c(max_steps);
00866
Vec g(max_steps);
00867
Vec x(max_steps);
00868
computeCostAndDerivative(0,
this, c[0], g[0]);
00869
x[0] = 0;
00870 real step = initial_step;
00871 real x_2 =
x[0]*
x[0];
00872 real x_3 =
x[0] * x_2;
00873 real x_4 = x_2 * x_2;
00874 real sum_x =
x[0];
00875 real sum_x_2 = x_2;
00876 real sum_x_3 = x_3;
00877 real sum_x_4 = x_4;
00878 real sum_c_x_2 = c[0] * x_2;
00879 real sum_g_x = g[0] *
x[0];
00880 real sum_c_x = c[0] *
x[0];
00881 real sum_c = c[0];
00882 real sum_g = g[0];
00883
for (
int i=1; i<max_steps; i++) {
00884
computeCostAndDerivative(step,
this, c[i], g[i]);
00885
x[i] = step;
00886
if (
abs(g[i]) < low_enough) {
00887
00888
return step;
00889 }
00890 x_2 =
x[i]*
x[i];
00891 x_3 =
x[i] * x_2;
00892 x_4 = x_2 * x_2;
00893 sum_x +=
x[i];
00894 sum_x_2 += x_2;
00895 sum_x_3 += x_3;
00896 sum_x_4 += x_4;
00897 sum_c_x_2 += c[i] * x_2;
00898 sum_g_x += g[i] *
x[i];
00899 sum_c_x += c[i] *
x[i];
00900 sum_c += c[i];
00901 sum_g += g[i];
00902 step =
findMinWithQuadInterpol(i+1, sum_x, sum_x_2, sum_x_3, sum_x_4, sum_c_x_2, sum_g_x, sum_c_x, sum_c, sum_g);
00903 }
00904 cout <<
"Warning : minimum not reached, is the cost really quadratic ?" <<
endl;
00905
return step;
00906 }
00907
00909
00911 real ConjGradientOptimizer::optimize()
00912 {
00913 cout <<
"Warning ! In ConjGradientOptimizer::optimize This method is deprecated, use optimizeN instead" <<
endl;
00914 ofstream out;
00915
if (!filename.empty()) {
00916 out.open(filename.c_str());
00917 }
00918
Vec lastmeancost(cost->size());
00919 early_stop =
false;
00920
00921
00922 computeOppositeGradient(
this,
current_opp_gradient);
00923
search_direction <<
current_opp_gradient;
00924
last_improvement = 0.1;
00925 real
last_cost, current_cost;
00926 cost->fprop();
00927 last_cost = cost->value[0];
00928 real df;
00929
00930
00931
for (
int t=0; !early_stop && t<nupdates; t++) {
00932
00933
00934
00935
00936 early_stop =
lineSearch();
00937
if (early_stop)
00938 cout <<
"Early stopping triggered by the line search" <<
endl;
00939 cost->fprop();
00940 current_cost = cost->value[0];
00941
00942
00943
bool early_stop_dir =
findDirection();
00944
if (early_stop_dir)
00945 cout <<
"Early stopping triggered by direction search" <<
endl;
00946 early_stop = early_stop || early_stop_dir;
00947
00948
last_improvement = last_cost - current_cost;
00949 last_cost = current_cost;
00950
00951
00952 df =
max (
last_improvement, 10*
stop_epsilon);
00953
current_step_size =
min(1.0, 2.0 * df /
dot(
search_direction, current_opp_gradient));
00954
00955
00956
if (
compute_cost) {
00957
meancost += cost->value;
00958 }
00959
if ((every!=0) && ((t+1)%every==0))
00960
00961 {
00962
00963
if (
compute_cost) {
00964
meancost /= real(every);
00965 }
00966
00967
00968
if (
compute_cost) {
00969
printStep(cerr, t+1,
meancost[0]);
00970 }
00971
if (
compute_cost && out) {
00972
printStep(out, t+1,
meancost[0]);
00973 }
00974
bool early_stop_mesure =
false;
00975
if (
compute_cost) {
00976 early_stop_mesure = measure(t+1,
meancost);
00977 }
00978
if (early_stop_dir)
00979 cout <<
"Early stopping triggered by the measurer" <<
endl;
00980 early_stop = early_stop || early_stop_mesure;
00981
00982
00983
if (
compute_cost) {
00984 lastmeancost <<
meancost;
00985 meancost.
clear();
00986 }
00987 }
00988 }
00989
return lastmeancost[0];
00990 }
00991
00992
00994
00996 bool ConjGradientOptimizer::optimizeN(
VecStatsCollector& stats_coll) {
00997 real df, current_cost;
00998
meancost.
clear();
00999
int stage_max = stage + nstages;
01000
01001
#ifdef BOUNDCHECK
01002
if (
current_step_size <= 0) {
01003
PLERROR(
"In ConjGradientOptimizer::optimizeN - current_step_size <= 0, have you called reset() ?");
01004 }
01005
#endif
01006
01007
if (stage==0)
01008 {
01009 computeOppositeGradient(
this,
current_opp_gradient);
01010
search_direction <<
current_opp_gradient;
01011
last_cost = cost->value[0];
01012 }
01013
01014
if (early_stop)
01015 stats_coll.
update(cost->value);
01016
01017
for (; !early_stop && stage<stage_max; stage++) {
01018
01019
01020 early_stop =
lineSearch();
01021 computeOppositeGradient(
this,
delta);
01022 current_cost = cost->value[0];
01023
if (
compute_cost) {
01024
meancost += cost->value;
01025 }
01026 stats_coll.
update(cost->value);
01027
01028
01029 early_stop = early_stop ||
findDirection();
01030
01031
last_improvement =
last_cost - current_cost;
01032
last_cost = current_cost;
01033
01034
01035 df =
max (
last_improvement, 10*
stop_epsilon);
01036
current_step_size =
min(1.0, 2.0*df /
dot(
search_direction,
current_opp_gradient));
01037
01038 }
01039
01040
if (
compute_cost) {
01041
meancost /= real(nstages);
01042
printStep(cerr, stage,
meancost[0]);
01043 early_stop = early_stop || measure(stage+1,
meancost);
01044 }
01045
01046
01047
if (early_stop)
01048 cout <<
"Early Stopping !" <<
endl;
01049
return early_stop;
01050 }
01051
01053
01055 real ConjGradientOptimizer::polakRibiere (
01056
void (*grad)(
Optimizer*,
const Vec&),
01057
ConjGradientOptimizer* opt) {
01058
int i;
01059
01060 real normg =
pownorm(opt->
current_opp_gradient);
01061
for (i=0; i<opt->
current_opp_gradient.
length(); i++) {
01062 opt->
tmp_storage[i] = opt->
delta[i] - opt->
current_opp_gradient[i];
01063 }
01064 real gamma =
dot(opt->
tmp_storage,opt->
delta) / normg;
01065
return gamma;
01066 }
01067
01069
01071 void ConjGradientOptimizer::quadraticInterpol(
01072 real f0, real f1, real g0,
01073 real& a, real& b, real& c) {
01074 c = f0;
01075 b = g0;
01076 a = f1 - f0 - g0;
01077 }
01078
01080
01082 void ConjGradientOptimizer::reset() {
01083 inherited::reset();
01084 early_stop =
false;
01085
last_improvement = 0.1;
01086
current_step_size =
starting_step_size;
01087 }
01088
01090
01092 void ConjGradientOptimizer::updateSearchDirection(real gamma) {
01093
if (gamma==0)
01094
search_direction <<
delta;
01095
else
01096
for (
int i=0; i<
search_direction.
length(); i++)
01097
search_direction[i] = delta[i] + gamma *
search_direction[i];
01098
current_opp_gradient << delta;
01099 }
01100
01101 }