Skip to content

Commit 3b4e1e2

Browse files
rcurtinzoq
andauthored
Test FP16 support when available with Armadillo (#426)
* Add CMake modules to find Bandicoot libraries and includes. * Add initial GitHub Actions to build against Bandicoot and run the tests. * 1. Add type proxies to select the right type based on the used backend and datatype. 2. Update IQN optimizer to build against Armadillo and Bandicoot. 3. Update IQN test cases to test against Armadillo and Bandicoot backend. 4. Update logisitic regression function, to support Armadillo and Bandicoot. * Refactor CNE optimizer and tests cases to work with Armadillo and Bandicoot datatypes. * Refactor Katyusha optimizer and tests cases to work with Armadillo and Bandicoot datatypes. * Refactor optimizers to work with Armadillo and Bandicoot datatypes: 1. AdaBelief 2. AdaBound 3. AMSBound 4. AdaGrad 5. Adam 6. Adamax 7. AMSGrad 8. Nadam 9. Nadamax 10. OptimisticAdam 11. Eve 12. FTML 13. LBFGS 14. Lookahead 15. Padam 16. QHAdam 17. SMORMS3 18. SpaleraSGD 19. SPSA 20. SWATS 21. WNGrad 22. Yogi 23. MomentumSGD 24. NesterovMomentum 25. QuasiHyperbolic 26. SGD 27. SGDR 28. SnapshotSGDR 29. CNE 30. CMAES 31. ActiveCMAES 32. PSO 33. LBestPSO 34. MOEAD 35. AugmentedLagrangian * Refactor optimizers to work with Armadillo and Bandicoot datatypes: 1. AdaDelta 2. AdaSqrt 3. BigBatchSGD 4. CoordinateDescent (CD) 5. GradientDescent 6. GridSearch 7. NSGA2 8. ParallelSGD * Refactor optimizers to work with Armadillo and Bandicoot datatypes: 1. DifferentialEvolution (DE) 2. RMSProp 3. SimulatedAnnealing (SA) 4. SARAH 5. SARAH+ 6. SVRG 7. BarzilaiBorweinDecay 8. DemonAdam 9. DemonSGD * Use inverse transform sampling to replicating arma::randg sampling since randg is not supported in Bandicoot. * Apply suggestions from code review by @rcurtin. Co-authored-by: Ryan Curtin <ryan@ratml.org> * Group test cases and fix indentations. * Update optimizer reference styling. * Add option to build against Bandicoot (by default we build against Bandicoot if found). * Mention AugLagrangianType constructor in the optimizer documentation, that allows to use a different datatype i.e. arma::fvec or coot::fvec. * Use data type specific element type for the bounds parameter. * Make sure we follow the max character line limitation from the style guide. Co-authored-by: Ryan Curtin <ryan@ratml.org> * Remove backports for outdates armadillo versions. * Indentation fixes. * Indentation fixes. * Use the correct data type during testing. * Do not use the namespace twice. * Test against CPU/OpenCL/CUDA backend. * Use the correct test case type. * We don't need Bandicoot when we test against Armadillo (CPU) backend. * Use the correct minimal Bandicoot version. Co-authored-by: Ryan Curtin <ryan@ratml.org> * Comment on the added proxies. * Merge arma and coot traits into a single file. * Fix deprecation warning from newer Armadillo. * Fix spacing. * Add logging output for PSO. * Adapt problems to be templatized. * Templatize tests to use ENS_TEST_TYPES. * Fix name of AdaGrad in the documentation. * Apply suggestions from code review. Co-authored-by: Ryan Curtin <ryan@ratml.org> * Update about half the optimizers to use FP16. * Refactor ForwardType into two different structures to avoid std::conditional. * Minor changes to get tests to pass on the Pi. * Remove unused proxies. * Update the other half of the optimizers to use FP16. * Minor fixes to work on non-emulated FP16. * Update to arma::hmat and enable sparse types. * Make CMake standard easily configurable. * Revert CategoryType to arma::Row<size_t> since that should always stay on the CPU. * Add convenience IsMatrixType<> struct. * Catch underflow for RMSprop. * Revert to norm()-only implementation for gradient norm computation. * Remove proxies, use using instead. * Older Armadillo versions don't support changing sparse-to-dense and also element types. * Remove sort_index() proxy and use using instead. * Oops, fix compatibility conv_to implementation. * Fix divide by zero condition. * Individually accessed vector, so keep vector on the CPU, for faster access. * Apply suggestions from code review Co-authored-by: Ryan Curtin <ryan@ratml.org> * Kepp the indicies on the CPU. * Apply suggestions from code review. Co-authored-by: Ryan Curtin <ryan@ratml.org> * Remove outdated test case. * Use initialization list for the velocity vector. * Apply suggestions from code review Co-authored-by: Ryan Curtin <ryan@ratml.org> * Readd removed test cases for MOEAD. * Use sort_index for sorting that is supported for both armadillo and bandicoot. * Revert changes to Parallel SGD optimizer, no benefit to upload to the GPU. * Only reset if we haven't reached the end of the iterations. * Use accu to the the sum of the vector. * Use clamp instead of min, for a more efficient computation. * Add missing data type. * Use ENS_HAVE_COOT instead of USE_COOT and automatically detect when Bandicoot has already been included. * Fix incorrect initialization list. * Remove unnecessary GetFillType<MatType>::zeros, which does not work for sparse matrices. * Clean up the last few proxy functions. * Refactor problems to avoid adding extra template parameters. * NSGA2: introduce a new Optimize function that takes the front and the paretoSet, so we can avoid introducing new template parameters. * MOEAD: introduce a new Optimize function that takes the front and the paretoSet, so we can avoid introducing new template parameters. * Remove unnecessary lr matrix. * Minor cleanups for SA. * Clean up member initialization for PSO. * Update documentation for multi-objective optimizers. * Update API for Snapshots(). * Keep the MOEAD front indices on the CPU, to be more efficient. * Remove class template parameter for the bound parameter, to make the optimizer class easier. * Use the correct location for the bandicoot wrapper. * No need to set the backend for testing, we will test whatever was available during compilation. * Use the correct def to enable the bandicoot backend. * use the correct name for the def. * Fixes so that Bandicoot actually gets detected and compiled in correctly. * Oops, fix merge. * Add tolerances for Bandicoot matrices. * Fix missing typedef. * Fix some failing tests. * Fix minor compilation issues. * Fix compilation errors and warnings. * Minor issues discovered in self-review. * Oops, fix missing type. * Reduce batch size to try and prevent NaNs. --------- Co-authored-by: Marcus Edel <marcus@urgs.org> Co-authored-by: Marcus Edel <marcus.edel@fu-berlin.de>
1 parent 8579b39 commit 3b4e1e2

156 files changed

Lines changed: 3423 additions & 2395 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMakeLists.txt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@ option(USE_BANDICOOT "If available, build against Bandicoot for GPU support." ON
1414

1515
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake")
1616

17-
# Set required C++ standard to C++14.
18-
set(CMAKE_CXX_STANDARD 14)
19-
set(CMAKE_CXX_STANDARD_REQUIRED ON)
17+
# Set minimum required C++ standard to C++14.
18+
if (NOT CMAKE_CXX_STANDARD)
19+
set(CMAKE_CXX_STANDARD 14)
20+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
21+
elseif (${CMAKE_CXX_STANDARD} LESS 14)
22+
message(FATAL_ERROR "ensmallen requires C++14 or newer!")
23+
endif ()
2024

2125
# Extract version from sources.
2226
set(ENSMALLEN_VERSION_FILE_NAME "${PROJECT_SOURCE_DIR}/include/ensmallen_bits/ens_version.hpp")

doc/optimizers.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ optimizer.Optimize(f, coordinates);
293293
* [AdaGrad](#adagrad)
294294
* [Differentiable separable functions](#differentiable-separable-functions)
295295

296-
## Adagrad
296+
## AdaGrad
297297

298298
*An optimizer for [differentiable separable functions](#differentiable-separable-functions).*
299299

@@ -1297,7 +1297,7 @@ total contribution of a gradient to all future updates.
12971297
* `DemonAdam()`
12981298
* `DemonAdam(`_`stepSize, batchSize`_`)`
12991299
* `DemonAdam(`_`stepSize, batchSize, momentum, beta1, beta2, eps, maxIterations, tolerance, shuffle`_`)`
1300-
* `DemonAdam(`_`stepSize, batchSize, momentum, beta1, beta2, eps, maxIterations, tolerance, shuffle, resetPolicy`_`)`
1300+
* `DemonAdam(`_`stepSize, batchSize, momentum, beta1, beta2, eps, maxIterations, tolerance, shuffle, resetPolicy, exactObjective`_`)`
13011301

13021302
Note that the `DemonAdam` class is based on
13031303
the `DemonAdamType<`_`UpdateRule`_`>` class with _`UpdateRule`_` = AdamUpdate`.
@@ -1329,12 +1329,20 @@ For convenience the following typedefs have been defined:
13291329
| `double` | **`tolerance`** | Maximum absolute tolerance to terminate algorithm. | `1e-5` |
13301330
| `bool` | **`shuffle`** | If true, the function order is shuffled; otherwise, each function is visited in linear order. | `true` |
13311331
| `bool` | **`resetPolicy`** | If true, parameters are reset before every Optimize call; otherwise, their values are retained. | `true` |
1332+
| `bool` | **`exactObjective`** | Calculate the exact objective at the end of optimization. (This could be computationally expensive!) | `false` |
13321333

13331334
The attributes of the optimizer may also be modified via the member methods
13341335
`StepSize()`, `BatchSize()`, `Momentum()`, `MomentumIterations()`, `Beta1()`,
13351336
`Beta2()`, `Eps()`, `MaxIterations()`, `Tolerance()`, `Shuffle()`, and
13361337
`ResetPolicy()`.
13371338

1339+
***Note:*** if `exactObjective` is `false`, then `Optimize(f, coordinates)` will
1340+
return an estimate of the objective function. This estimate is the sum of the
1341+
objectives obtained on the last pass of the separable functions. The estimate
1342+
will not include contributions from any separable functions not visited in the
1343+
last pass (e.g., if `maxIterations` is not an integer multiple of
1344+
`f.NumFunctions()`).
1345+
13381346
#### Examples
13391347

13401348
<details open>
@@ -1386,7 +1394,7 @@ optimizer:
13861394
* `DemonSGD()`
13871395
* `DemonSGD(`_`stepSize, batchSize`_`)`
13881396
* `DemonSGD(`_`stepSize, batchSize, momentum, maxIterations, tolerance, shuffle`_`)`
1389-
* `DemonSGD(`_`stepSize, batchSize, momentum, maxIterations, tolerance, shuffle, resetPolicy`_`)`
1397+
* `DemonSGD(`_`stepSize, batchSize, momentum, maxIterations, tolerance, shuffle, resetPolicy, exactObjective`_`)`
13901398
13911399
#### Attributes
13921400
@@ -1399,11 +1407,19 @@ optimizer:
13991407
| `double` | **`tolerance`** | Maximum absolute tolerance to terminate algorithm. | `1e-5` |
14001408
| `bool` | **`shuffle`** | If true, the function order is shuffled; otherwise, each function is visited in linear order. | `true` |
14011409
| `bool` | **`resetPolicy`** | If true, parameters are reset before every Optimize call; otherwise, their values are retained. | `true` |
1410+
| `bool` | **`exactObjective`** | Calculate the exact objective at the end of optimization. (This could be computationally expensive!) | `false` |
14021411
14031412
The attributes of the optimizer may also be modified via the member methods
14041413
`StepSize()`, `BatchSize()`, `Momentum()`, `MomentumIterations()`,
14051414
`MaxIterations()`, `Tolerance()`, `Shuffle()`, and `ResetPolicy()`.
14061415
1416+
***Note:*** if `exactObjective` is `false`, then `Optimize(f, coordinates)` will
1417+
return an estimate of the objective function. This estimate is the sum of the
1418+
objectives obtained on the last pass of the separable functions. The estimate
1419+
will not include contributions from any separable functions not visited in the
1420+
last pass (e.g., if `maxIterations` is not an integer multiple of
1421+
`f.NumFunctions()`).
1422+
14071423
#### Examples
14081424
14091425
<details open>

include/ensmallen_bits/ada_belief/ada_belief_update.hpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ class AdaBeliefUpdate
7979
class Policy
8080
{
8181
public:
82+
typedef typename MatType::elem_type ElemType;
83+
8284
/**
8385
* This constructor is called by the SGD Optimize() method before the start
8486
* of the iteration update process.
@@ -89,10 +91,16 @@ class AdaBeliefUpdate
8991
*/
9092
Policy(AdaBeliefUpdate& parent, const size_t rows, const size_t cols) :
9193
parent(parent),
94+
beta1(ElemType(parent.beta1)),
95+
beta2(ElemType(parent.beta2)),
96+
epsilon(ElemType(parent.epsilon)),
9297
iteration(0)
9398
{
9499
m.zeros(rows, cols);
95100
s.zeros(rows, cols);
101+
// Prevent underflow.
102+
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
103+
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
96104
}
97105

98106
/**
@@ -109,18 +117,18 @@ class AdaBeliefUpdate
109117
// Increment the iteration counter variable.
110118
++iteration;
111119

112-
m *= parent.beta1;
113-
m += (1 - parent.beta1) * gradient;
120+
m *= beta1;
121+
m += (1 - beta1) * gradient;
114122

115-
s *= parent.beta2;
116-
s += (1 - parent.beta2) * pow(gradient - m, 2.0) + parent.epsilon;
123+
s *= beta2;
124+
s += (1 - beta2) * pow(gradient - m, 2) + epsilon;
117125

118-
const double biasCorrection1 = 1.0 - std::pow(parent.beta1, iteration);
119-
const double biasCorrection2 = 1.0 - std::pow(parent.beta2, iteration);
126+
const ElemType biasCorrection1 = 1 - std::pow(beta1, ElemType(iteration));
127+
const ElemType biasCorrection2 = 1 - std::pow(beta2, ElemType(iteration));
120128

121129
// And update the iterate.
122-
iterate -= ((m / biasCorrection1) * stepSize) / (sqrt(s /
123-
biasCorrection2) + parent.epsilon);
130+
iterate -= ((m / biasCorrection1) * ElemType(stepSize)) /
131+
(sqrt(s / biasCorrection2) + epsilon);
124132
}
125133

126134
private:
@@ -133,6 +141,11 @@ class AdaBeliefUpdate
133141
// The exponential moving average of squared gradient values.
134142
GradType s;
135143

144+
// Parent parameters converted to the element type of the matrix.
145+
ElemType beta1;
146+
ElemType beta2;
147+
ElemType epsilon;
148+
136149
// The number of iterations.
137150
size_t iteration;
138151
};

include/ensmallen_bits/ada_bound/ada_bound_update.hpp

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class AdaBoundUpdate
9696
class Policy
9797
{
9898
public:
99+
typedef typename MatType::elem_type ElemType;
100+
99101
/**
100102
* This constructor is called by the SGD Optimize() method before the start
101103
* of the iteration update process.
@@ -105,10 +107,24 @@ class AdaBoundUpdate
105107
* @param cols Number of columns in the gradient matrix.
106108
*/
107109
Policy(AdaBoundUpdate& parent, const size_t rows, const size_t cols) :
108-
parent(parent), first(true), initialStepSize(0), iteration(0)
110+
parent(parent),
111+
finalLr(ElemType(parent.finalLr)),
112+
gamma(ElemType(parent.gamma)),
113+
epsilon(ElemType(parent.epsilon)),
114+
beta1(ElemType(parent.beta1)),
115+
beta2(ElemType(parent.beta2)),
116+
first(true),
117+
initialStepSize(0),
118+
iteration(0)
109119
{
110120
m.zeros(rows, cols);
111121
v.zeros(rows, cols);
122+
123+
// Check for underflows in conversions.
124+
if (gamma == ElemType(0) && parent.gamma != 0.0)
125+
gamma = 10 * std::numeric_limits<ElemType>::epsilon();
126+
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
127+
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
112128
}
113129

114130
/**
@@ -129,30 +145,30 @@ class AdaBoundUpdate
129145
if (first)
130146
{
131147
first = false;
132-
initialStepSize = stepSize;
148+
initialStepSize = ElemType(stepSize);
133149
}
134150

135151
// Increment the iteration counter variable.
136152
++iteration;
137153

138154
// Decay the first and second moment running average coefficient.
139-
m *= parent.beta1;
140-
m += (1 - parent.beta1) * gradient;
155+
m *= beta1;
156+
m += (1 - beta1) * gradient;
141157

142-
v *= parent.beta2;
143-
v += (1 - parent.beta2) * (gradient % gradient);
158+
v *= beta2;
159+
v += (1 - beta2) * (gradient % gradient);
144160

145-
const ElemType biasCorrection1 = 1.0 - std::pow(parent.beta1, iteration);
146-
const ElemType biasCorrection2 = 1.0 - std::pow(parent.beta2, iteration);
161+
const ElemType biasCorrection1 = 1 - std::pow(beta1, ElemType(iteration));
162+
const ElemType biasCorrection2 = 1 - std::pow(beta2, ElemType(iteration));
147163

148-
const ElemType fl = parent.finalLr * stepSize / initialStepSize;
149-
const ElemType lower = fl * (1.0 - 1.0 / (parent.gamma * iteration + 1));
150-
const ElemType upper = fl * (1.0 + 1.0 / (parent.gamma * iteration));
164+
const ElemType fl = finalLr * ElemType(stepSize) / initialStepSize;
165+
const ElemType lower = fl * (1 - 1 / (gamma * iteration + 1));
166+
const ElemType upper = fl * (1 + 1 / (gamma * iteration));
151167

152168
// Applies bounds on actual learning rate.
153-
iterate -= clamp((stepSize *
154-
std::sqrt(biasCorrection2) / biasCorrection1) / (sqrt(v) +
155-
parent.epsilon), lower, upper) % m;
169+
iterate -= clamp((ElemType(stepSize) *
170+
std::sqrt(biasCorrection2) / biasCorrection1) / (sqrt(v) + epsilon),
171+
lower, upper) % m;
156172
}
157173

158174
private:
@@ -165,11 +181,18 @@ class AdaBoundUpdate
165181
// The exponential moving average of squared gradient values.
166182
GradType v;
167183

184+
// Parameters of the parent, casted to the element type of the problem.
185+
ElemType finalLr;
186+
ElemType gamma;
187+
ElemType epsilon;
188+
ElemType beta1;
189+
ElemType beta2;
190+
168191
// Whether this is the first call of the Update method.
169192
bool first;
170193

171194
// The initial (Adam) learning rate.
172-
double initialStepSize;
195+
ElemType initialStepSize;
173196

174197
// The number of iterations.
175198
size_t iteration;

include/ensmallen_bits/ada_bound/ams_bound_update.hpp

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class AMSBoundUpdate
9696
class Policy
9797
{
9898
public:
99+
typedef typename MatType::elem_type ElemType;
100+
99101
/**
100102
* This constructor is called by the SGD Optimize() method before the start
101103
* of the iteration update process.
@@ -105,11 +107,25 @@ class AMSBoundUpdate
105107
* @param cols Number of columns in the gradient matrix.
106108
*/
107109
Policy(AMSBoundUpdate& parent, const size_t rows, const size_t cols) :
108-
parent(parent), first(true), initialStepSize(0), iteration(0)
110+
parent(parent),
111+
finalLr(ElemType(parent.finalLr)),
112+
gamma(ElemType(parent.gamma)),
113+
epsilon(ElemType(parent.epsilon)),
114+
beta1(ElemType(parent.beta1)),
115+
beta2(ElemType(parent.beta2)),
116+
first(true),
117+
initialStepSize(0),
118+
iteration(0)
109119
{
110120
m.zeros(rows, cols);
111121
v.zeros(rows, cols);
112122
vImproved.zeros(rows, cols);
123+
124+
// Check for underflows in conversions.
125+
if (gamma == ElemType(0) && parent.gamma != 0.0)
126+
gamma = 10 * std::numeric_limits<ElemType>::epsilon();
127+
if (epsilon == ElemType(0) && parent.epsilon != 0.0)
128+
epsilon = 10 * std::numeric_limits<ElemType>::epsilon();
113129
}
114130

115131
/**
@@ -123,40 +139,36 @@ class AMSBoundUpdate
123139
const double stepSize,
124140
const GradType& gradient)
125141
{
126-
// Convenience typedefs.
127-
typedef typename MatType::elem_type ElemType;
128-
129142
// Save the initial step size.
130143
if (first)
131144
{
132145
first = false;
133-
initialStepSize = stepSize;
146+
initialStepSize = ElemType(stepSize);
134147
}
135148

136149
// Increment the iteration counter variable.
137150
++iteration;
138151

139152
// Decay the first and second moment running average coefficient.
140-
m *= parent.beta1;
141-
m += (1 - parent.beta1) * gradient;
153+
m *= beta1;
154+
m += (1 - beta1) * gradient;
142155

143-
v *= parent.beta2;
144-
v += (1 - parent.beta2) * (gradient % gradient);
156+
v *= beta2;
157+
v += (1 - beta2) * (gradient % gradient);
145158

146-
const ElemType biasCorrection1 = 1.0 - std::pow(parent.beta1, iteration);
147-
const ElemType biasCorrection2 = 1.0 - std::pow(parent.beta2, iteration);
159+
const ElemType biasCorrection1 = 1 - std::pow(beta1, ElemType(iteration));
160+
const ElemType biasCorrection2 = 1 - std::pow(beta2, ElemType(iteration));
148161

149-
const ElemType fl = parent.finalLr * stepSize / initialStepSize;
150-
const ElemType lower = fl * (1.0 - 1.0 / (parent.gamma * iteration + 1));
151-
const ElemType upper = fl * (1.0 + 1.0 / (parent.gamma * iteration));
162+
const ElemType fl = finalLr * ElemType(stepSize) / initialStepSize;
163+
const ElemType lower = fl * (1 - 1 / (gamma * iteration + 1));
164+
const ElemType upper = fl * (1 + 1 / (gamma * iteration));
152165

153166
// Element wise maximum of past and present squared gradients.
154167
vImproved = max(vImproved, v);
155168

156169
// Applies bounds on actual learning rate.
157-
iterate -= clamp((stepSize * std::sqrt(biasCorrection2) /
158-
biasCorrection1) / (sqrt(vImproved) + parent.epsilon),
159-
lower, upper) % m;
170+
iterate -= clamp((ElemType(stepSize) * std::sqrt(biasCorrection2) /
171+
biasCorrection1) / (sqrt(vImproved) + epsilon), lower, upper) % m;
160172
}
161173

162174
private:
@@ -169,11 +181,18 @@ class AMSBoundUpdate
169181
// The exponential moving average of squared gradient values.
170182
GradType v;
171183

184+
// Parameters of the parent, casted to the element type of the problem.
185+
ElemType finalLr;
186+
ElemType gamma;
187+
ElemType epsilon;
188+
ElemType beta1;
189+
ElemType beta2;
190+
172191
// Whether this is the first call of the Update method.
173192
bool first;
174193

175194
// The initial (Adam) learning rate.
176-
double initialStepSize;
195+
ElemType initialStepSize;
177196

178197
// The optimal squared gradient value.
179198
GradType vImproved;

0 commit comments

Comments
 (0)