1
1
import * as tensorflow from '@tensorflow/tfjs' ;
2
- import * as math from 'mathjs' ;
3
- import { DataSet , ModelDict , SequentialModelParameters , datasetType , BaysianOptimisationStep , LossFunction , DomainPointValue } from '../types/types' ;
2
+ import { ModelDict , SequentialModelParameters , DataPoint , BaysianOptimisationStep , LossFunction , DomainPointValue } from '../types/types' ;
4
3
import * as bayesianOptimizer from './bayesianOptimizer' ;
5
4
import * as gridSearchOptimizer from './gridSearchOptimizer' ;
6
5
import * as paramspace from './paramspace' ;
7
6
import * as priors from './priors' ;
7
+ import * as modelEvaluator from './modelEvaluater' ;
8
8
9
9
class AutotunerBaseClass {
10
- dataset : DataSet ;
11
10
metrics : string [ ] = [ ] ;
12
11
observedValues : DomainPointValue [ ] = [ ] ;
13
12
/**
@@ -24,14 +23,15 @@ class AutotunerBaseClass {
24
23
paramspace : any ;
25
24
optimizer : any ;
26
25
priors : any ;
26
+ modelEvaluator : any ;
27
27
28
28
/**
29
29
* Returns the value of a domain point.
30
30
*
31
31
* @param {number } domainIndex Index of the domain point to be evaluated.
32
32
* @return {Promise<number> } Value of the domain point
33
33
*/
34
- evaluateModel : ( domainIndex : number ) => Promise < number > ;
34
+ evaluateModel : ( domainIndex : number , objective : string , useCrossValidation : boolean ) => Promise < number > ;
35
35
36
36
/**
37
37
* Decide whether to continue tuning the hyperparameters.
@@ -57,44 +57,63 @@ class AutotunerBaseClass {
57
57
}
58
58
}
59
59
60
- constructor ( metrics : string [ ] , trainingSet : datasetType , testSet : datasetType , evaluationSet : datasetType , numberOfCategories : number ) {
60
+ checkObjective ( objective : string ) : boolean {
61
+ const allowedObjectives : string [ ] = [ 'error' ] . concat ( this . metrics ) ;
62
+ if ( ! allowedObjectives . includes ( objective ) ) {
63
+ console . log ( "Invalid objective function selected!" ) ;
64
+ console . log ( "Objective function must be one of the following: " + allowedObjectives . join ( ) ) ;
65
+ return true ;
66
+ }
67
+ return false ;
68
+ }
69
+
70
+ constructor ( metrics : string [ ] , dataSet : DataPoint [ ] , numberOfCategories : number , validationSetRatio : number = 0.25 , testSetRatio : number = 0.25 ) {
61
71
this . paramspace = new paramspace . Paramspace ( ) ;
72
+ this . modelEvaluator = new modelEvaluator . ModelEvaluater ( dataSet , numberOfCategories , validationSetRatio , testSetRatio ) ;
62
73
this . metrics = metrics ;
63
-
64
- const dataset : DataSet = { trainingSet : trainingSet , testSet : testSet , evaluationSet : evaluationSet , numberOfCategories : numberOfCategories } ;
65
- this . dataset = dataset ;
66
74
}
67
75
68
76
/**
69
77
* Search the best Parameters using bayesian optimization.
70
78
*
79
+ * @param {string } [objective='error'] Define the objective of the optimization. Set to 'error' by default.
80
+ * @param {boolean } [useCrossValidation=false] Indicate wheter or not to use cross validation to evaluate the model. Set to 'false' by default.
71
81
* @param {number } [maxIteration=0.75] Fraction of domain points that should be evaluated at most. (e.g. for 'maxIteration=0.75' the optimization stops if 75% of the domain has been evaluated)
72
82
* @param {boolean } [stopingCriteria] Predicate on the observed values when to stop the optimization
73
83
*/
74
- async bayesianOptimization ( maxIteration : number = 0.75 , stopingCriteria ?: ( ( observedValues : DomainPointValue [ ] ) => boolean ) ) {
84
+ async bayesianOptimization ( objective : string = 'error' , useCrossValidation : boolean = false , maxIteration : number = 0.75 , stopingCriteria ?: ( ( observedValues : DomainPointValue [ ] ) => boolean ) ) {
85
+ if ( this . checkObjective ( objective ) ) {
86
+ return ;
87
+ }
75
88
this . initializePriors ( ) ;
76
89
this . optimizer = new bayesianOptimizer . Optimizer ( this . paramspace . domainIndices , this . paramspace . modelsDomains , this . priors . mean , this . priors . kernel ) ;
77
90
this . maxIterations = maxIteration ;
78
91
if ( stopingCriteria ) {
79
92
this . metricsStopingCriteria = stopingCriteria ;
80
93
}
81
94
82
- this . tuneHyperparameters ( ) ;
95
+ this . tuneHyperparameters ( objective , useCrossValidation ) ;
83
96
}
84
97
85
98
/**
86
99
* Search the best Parameters using grid search.
100
+ *
101
+ * @param {string } [objective='error'] Define the objective of the optimization. Set to 'error' by default.
102
+ * @param {boolean } [useCrossValidation=false] Indicate wheter or not to use cross validation to evaluate the model. Set to 'false' by default.
87
103
*/
88
- async gridSearchOptimizytion ( ) {
104
+ async gridSearchOptimizytion ( objective : string = 'error' , useCrossValidation : boolean = false ) {
105
+ if ( this . checkObjective ( objective ) ) {
106
+ return ;
107
+ }
89
108
this . initializePriors ( ) ;
90
109
this . optimizer = new gridSearchOptimizer . Optimizer ( this . paramspace . domainIndices , this . paramspace . modelsDomains ) ;
91
110
this . maxIterations = 1 ;
92
111
93
- this . tuneHyperparameters ( ) ;
112
+ this . tuneHyperparameters ( objective , useCrossValidation ) ;
94
113
}
95
114
96
115
97
- async tuneHyperparameters ( ) {
116
+ async tuneHyperparameters ( objective : string , useCrossValidation : boolean ) {
98
117
console . log ( "============================" ) ;
99
118
console . log ( "tuning the hyperparameters" ) ;
100
119
@@ -104,13 +123,12 @@ class AutotunerBaseClass {
104
123
var nextOptimizationPoint : BaysianOptimisationStep = this . optimizer . getNextPoint ( ) ;
105
124
106
125
// Train a model given the params and obtain a quality metric value.
107
- var value = await this . evaluateModel ( nextOptimizationPoint . nextPoint ) ;
126
+ var value = await this . evaluateModel ( nextOptimizationPoint . nextPoint , objective , useCrossValidation ) ;
108
127
109
128
// Report the obtained quality metric value.
110
129
this . optimizer . addSample ( nextOptimizationPoint . nextPoint , value ) ;
111
130
112
131
optimizing = this . stopingCriteria ( ) ;
113
-
114
132
}
115
133
// keep observations for the next optimization run
116
134
this . priors . commit ( this . paramspace . observedValues ) ;
@@ -123,10 +141,19 @@ class AutotunerBaseClass {
123
141
class TensorflowlModelAutotuner extends AutotunerBaseClass {
124
142
modelDict : ModelDict = { } ;
125
143
126
- constructor ( metrics : string [ ] , trainingSet : datasetType , testSet : datasetType , evaluationSet : datasetType , numberOfCategories : number ) {
127
- super ( metrics , trainingSet , testSet , evaluationSet , numberOfCategories ) ;
144
+ /**
145
+ * Initialize the autotuner.
146
+ *
147
+ * @param {string[] } metrics
148
+ * @param {DataPoint[] } dataSet
149
+ * @param {number } numberOfCategories
150
+ * @param {number=0.25 } validationSetRatio
151
+ * @param {number=0.25 } testSetRatio
152
+ */
153
+ constructor ( metrics : string [ ] , dataSet : DataPoint [ ] , numberOfCategories : number , validationSetRatio : number = 0.25 , testSetRatio : number = 0.25 ) {
154
+ super ( metrics , dataSet , numberOfCategories , validationSetRatio , testSetRatio ) ;
128
155
129
- this . evaluateModel = async ( point : number ) => {
156
+ this . evaluateModel = async ( point : number , objective : string , useCrossValidation : boolean ) => {
130
157
const modelIdentifier = this . paramspace . domain [ point ] [ 'model' ] ;
131
158
const model = this . modelDict [ modelIdentifier ] ;
132
159
const params = this . paramspace . domain [ point ] [ 'params' ] ;
@@ -143,22 +170,21 @@ class TensorflowlModelAutotuner extends AutotunerBaseClass {
143
170
optimizer : optimizerFunction
144
171
} ) ;
145
172
146
- let concatenatedTensorTrainData = tensorflow . tidy ( ( ) => tensorflow . concat ( this . dataset . trainingSet . data ) ) ;
147
- let concatenatedTrainLableData = tensorflow . tidy ( ( ) => tensorflow . oneHot ( this . dataset . trainingSet . lables , this . dataset . numberOfCategories ) ) ;
148
- await model . fit ( concatenatedTensorTrainData , concatenatedTrainLableData , args ) ;
149
-
150
- let concatenatedTensorTestData = tensorflow . tidy ( ( ) => tensorflow . concat ( this . dataset . trainingSet . data ) ) ;
151
- let concatenatedTestLables = tensorflow . tidy ( ( ) => tensorflow . oneHot ( this . dataset . trainingSet . lables , this . dataset . numberOfCategories ) ) ;
152
- const evaluationResult = model . evaluate ( concatenatedTensorTestData , concatenatedTestLables ) as tensorflow . Tensor [ ] ;
153
-
154
- const error = evaluationResult [ 0 ] . dataSync ( ) [ 0 ] ;
155
- const score = evaluationResult [ 1 ] . dataSync ( ) [ 0 ] ;
156
- // keep track of the scores
157
- this . observedValues . push ( { error : error , metricScores : [ score ] } ) ;
158
- return score ;
173
+ let dataPointValue : DomainPointValue = useCrossValidation
174
+ ? await this . modelEvaluator . EvaluateSequentialTensorflowModelCV ( model , args )
175
+ : await this . modelEvaluator . EvaluateSequentialTensorflowModel ( model , args ) ;
176
+ this . observedValues . push ( dataPointValue ) ;
177
+ return objective === 'error' ? dataPointValue . error : dataPointValue . metricScores [ 0 ] ;
159
178
}
160
179
}
161
180
181
+ /**
182
+ * Add a new model and its range of parameters to the autotuner.
183
+ *
184
+ * @param {string } modelIdentifier Identifier of the model
185
+ * @param {tensorflow.Sequential } model Actual Tensorflow model
186
+ * @param {SequentialModelParameters } modelParameters Parameters of the Model: define lossfunction, optimizer, algorithm batch size and number of traning epochs.
187
+ */
162
188
addModel ( modelIdentifier : string , model : tensorflow . Sequential , modelParameters : SequentialModelParameters ) {
163
189
this . modelDict [ modelIdentifier ] = model ;
164
190
0 commit comments