00a470f06e98c1bd342ff3fcdc16068e14cbb8c7
[platform/core/api/webapi-plugins.git] / src / ml / js / ml_trainer.js
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 var MachineLearningTrainer = function () { };
18
19 var OptimizerType = {
20     OPTIMIZER_ADAM: 'OPTIMIZER_ADAM',
21     OPTIMIZER_SGD: 'OPTIMIZER_SGD',
22     OPTIMIZER_UNKNOWN: 'OPTIMIZER_UNKNOWN'
23 };
24
25 var DatasetType = {
26     DATASET_GENERATOR: 'DATASET_GENERATOR',
27     DATASET_FILE: 'DATASET_FILE',
28     DATASET_UNKNOWN: 'DATASET_UNKNOWN'
29 };
30
31 var LayerType = {
32     LAYER_IN: 'LAYER_IN',
33     LAYER_FC: 'LAYER_FC',
34     LAYER_BN: 'LAYER_BN',
35     LAYER_CONV2D: 'LAYER_CONV2D',
36     LAYER_POOLING2D: 'LAYER_POOLING2D',
37     LAYER_FLATTEN: 'LAYER_FLATTEN',
38     LAYER_ACTIVATION: 'LAYER_ACTIVATION',
39     LAYER_ADDITION: 'LAYER_ADDITION',
40     LAYER_CONCAT: 'LAYER_CONCAT',
41     LAYER_MULTIOUT: 'LAYER_MULTIOUT',
42     LAYER_LOSS_MSE: 'LAYER_LOSS_MSE',
43     LAYER_LOSS_CROSS_ENTROPY_SIGMOID: 'LAYER_LOSS_CROSS_ENTROPY_SIGMOID',
44     LAYER_LOSS_CROSS_ENTROPY_SOFTMAX: 'LAYER_LOSS_CROSS_ENTROPY_SOFTMAX',
45     LAYER_BACKBONE_NNSTREAMER: 'LAYER_BACKBONE_NNSTREAMER',
46     LAYER_EMBEDDING: 'LAYER_EMBEDDING',
47     LAYER_RNN: 'LAYER_RNN',
48     LAYER_UNKNOWN: 'LAYER_UNKNOWN'
49 };
50
51 var VerbosityLevel = {
52     SUMMARY_MODEL: 'SUMMARY_MODEL',
53     SUMMARY_LAYER: 'SUMMARY_LAYER',
54     SUMMARY_TENSOR: 'SUMMARY_TENSOR'
55 };
56
57 var Layer = function(id, type) {
58     Object.defineProperties(this, {
59         name: {
60             enumerable: true,
61             get: function() {
62                 // TODO
63             }
64         },
65         type: {
66             enumerable: true,
67             writable: false,
68             value: type
69         },
70         _id: { value: id, writable: false, enumerable: false }
71     });
72 };
73
74 var ValidSetPropertyExceptions = [
75     'InvalidValuesError',
76     'TypeMismatchError',
77     'AbortError'
78 ];
79
80 Layer.prototype.setProperty = function() {
81     var args = validator_.validateArgs(arguments, [
82         {
83             name: 'name',
84             type: types_.STRING
85         },
86         {
87             name: 'value',
88             type: types_.STRING
89         }
90     ]);
91
92     if (!args.has.name || !args.has.value) {
93         throw new WebAPIException(
94             WebAPIException.TYPE_MISMATCH_ERR,
95             'Invalid parameter: ' + (args.has.name ? 'value' : 'name') + ' is undefined'
96         );
97     }
98
99     var callArgs = {
100         id: this._id,
101         name: args.name,
102         value: args.value
103     };
104
105     var result = native_.callSync('MLTrainerLayerSetProperty', callArgs);
106
107     if (native_.isFailure(result)) {
108         throw native_.getErrorObjectAndValidate(
109             result,
110             ValidSetPropertyExceptions,
111             AbortError
112         );
113     }
114 };
115
116 var Optimizer = function(id, type) {
117     Object.defineProperties(this, {
118         type: {
119             enumerable: true,
120             writable: false,
121             value: type
122         },
123         _id: { value: id, writable: false, enumerable: false }
124     });
125 };
126
127 Optimizer.prototype.setProperty = function() {
128     var args = validator_.validateArgs(arguments, [
129         {
130             name: 'name',
131             type: types_.STRING
132         },
133         {
134             name: 'value',
135             type: types_.STRING
136         }
137     ]);
138
139     if (!args.has.name || !args.has.value) {
140         throw new WebAPIException(
141             WebAPIException.TYPE_MISMATCH_ERR,
142             'Invalid parameter: ' + (args.has.name ? 'value' : 'name') + ' is undefined'
143         );
144     }
145
146     var callArgs = {
147         id: this._id,
148         name: args.name,
149         value: args.value
150     };
151
152     var result = native_.callSync('MLTrainerOptimizerSetProperty', callArgs);
153
154     if (native_.isFailure(result)) {
155         throw native_.getErrorObjectAndValidate(
156             result,
157             ValidSetPropertyExceptions,
158             AbortError
159         );
160     }
161 };
162
163 var Dataset = function(id, type) {
164     Object.defineProperties(this, {
165         type: {
166             enumerable: true,
167             writable: false,
168             value: type
169         },
170         _id: { value: id, writable: false, enumerable: false }
171     });
172 };
173
174 Dataset.prototype.setProperty = function() {
175     var args = validator_.validateArgs(arguments, [
176         {
177             name: 'name',
178             type: types_.STRING
179         },
180         {
181             name: 'value',
182             type: types_.STRING
183         }
184     ]);
185
186     if (!args.has.name || !args.has.value) {
187         throw new WebAPIException(
188             WebAPIException.TYPE_MISMATCH_ERR,
189             'Invalid parameter: ' + (args.has.name ? 'value' : 'name') + ' is undefined'
190         );
191     }
192
193     var callArgs = {
194         id: this._id,
195         name: args.name,
196         value: args.value
197     };
198
199     var result = native_.callSync('MLTrainerDatasetSetProperty', callArgs);
200
201     if (native_.isFailure(result)) {
202         throw native_.getErrorObjectAndValidate(
203             result,
204             ValidSetPropertyExceptions,
205             AbortError
206         );
207     }
208 };
209
210 var Model = function(id) {
211     Object.defineProperties(this, {
212         _id: { value: id, writable: false, enumerable: false }
213     });
214 };
215
216 function ValidateCompileOptions(options) {
217     var args = {};
218     if (options.hasOwnProperty('loss_val')) {
219         args.loss = options.loss_val;
220     }
221     if (options.hasOwnProperty('loss')) {
222         args.loss = options.loss;
223     }
224     if (options.hasOwnProperty('batch_size')) {
225         args.batch_size = options.batch_size;
226     }
227     return args;
228 }
229
230 var ValidModelCompileExceptions = [
231     'InvalidValuesError',
232     'TypeMismatchError',
233     'AbortError'
234 ];
235
236 Model.prototype.compile = function() {
237     var args = validator_.validateArgs(arguments, [
238         {
239             name: 'options',
240             type: validator_.Types.DICTIONARY,
241             optional: true,
242             nullable: true
243         }
244     ]);
245     var options = {};
246     if (args.has.options) {
247         options = ValidateCompileOptions(args.options);
248     }
249
250     var callArgs = {
251         id: this._id,
252         options: options
253     };
254
255     var result = native_.callSync('MLTrainerModelCompile', callArgs);
256
257     if (native_.isFailure(result)) {
258         throw native_.getErrorObjectAndValidate(
259             result,
260             ValidModelCompileExceptions,
261             AbortError
262         );
263     }
264 };
265
266 function ValidateRunOptions(options) {
267     var args = {};
268     if (options.hasOwnProperty('batch_size')) {
269         args.batch_size = options.batch_size;
270     }
271     if (options.hasOwnProperty('epochs')) {
272         args.epochs = options.epochs;
273     }
274     if (options.hasOwnProperty('save_path')) {
275         args.save_path = options.save_path;
276     }
277     return args;
278 }
279
280 var ValidModelRunExceptions = [
281     'InvalidValuesError',
282     'TypeMismatchError'
283 ];
284
285 Model.prototype.run = function() {
286     var args = validator_.validateArgs(arguments, [
287         {
288             name: 'options',
289             type: validator_.Types.DICTIONARY,
290             optional: true,
291             nullable: true
292         },
293         {
294             name: 'successCallback',
295             type: types_.FUNCTION
296         },
297         {
298             name: 'errorCallback',
299             type: types_.FUNCTION,
300             optional: true,
301             nullable: true
302         }
303     ]);
304     var runOptions = {};
305     if (args.has.options) {
306         runOptions = ValidateRunOptions(args.options);
307     }
308
309     var callArgs = {
310         id: this._id,
311         options: runOptions
312     };
313
314     var callback = function (result) {
315         if (native_.isFailure(result)) {
316             native_.callIfPossible(
317                 args.errorCallback,
318                 native_.getErrorObjectAndValidate(
319                     result,
320                     ValidModelRunExceptions,
321                     AbortError
322                 )
323             );
324         } else {
325             args.successCallback();
326         }
327     };
328
329     var result = native_.call('MLTrainerModelRun', callArgs, callback);
330     if (native_.isFailure(result)) {
331         throw native_.getErrorObjectAndValidate(
332             result,
333             ValidModelRunExceptions,
334             AbortError
335         );
336     }
337 };
338
339 Model.prototype.summarize = function() {
340     var args = validator_.validateArgs(arguments, [
341         {
342             name: 'level',
343             type: types_.ENUM,
344             values: Object.values(VerbosityLevel),
345             optional: true,
346             nullable: true
347         }
348     ]);
349
350     var callArgs = {
351         id: this._id,
352         level: args.level ? args.level : "SUMMARY_MODEL"
353     }
354
355     var result = native_.callSync('MLTrainerModelSummarize', callArgs);
356
357     if (native_.isFailure(result)) {
358         throw native_.getErrorObjectAndValidate(
359             result,
360             ValidBasicExceptions,
361             AbortError
362         );
363     }
364
365     return result.summary
366 };
367
368 var ValidBasicExceptions = [
369     'TypeMismatchError',
370     'AbortError'
371 ];
372
373 Model.prototype.addLayer = function() {
374     var args = validator_.validateArgs(arguments, [
375         {
376             name: 'layer',
377             type: types_.PLATFORM_OBJECT,
378             values: Layer
379         }
380     ]);
381
382     if (!args.has.layer) {
383         throw new WebAPIException(
384             WebAPIException.TYPE_MISMATCH_ERR, 'Invalid parameter: layer is undefined'
385         );
386     }
387
388     var callArgs = {
389         id: this._id,
390         layerId: args.layer._id
391     };
392
393     var result = native_.callSync('MLTrainerModelAddLayer', callArgs);
394
395     if (native_.isFailure(result)) {
396         throw native_.getErrorObjectAndValidate(
397             result,
398             ValidBasicExceptions,
399             AbortError
400         );
401     }
402 };
403
404 Model.prototype.setDataset = function() {
405     var args = validator_.validateArgs(arguments, [
406         {
407             name: 'dataset',
408             type: types_.PLATFORM_OBJECT,
409             values: Dataset
410         }
411     ]);
412
413     if (!args.has.dataset) {
414         throw new WebAPIException(
415             WebAPIException.TYPE_MISMATCH_ERR, 'Invalid parameter: dataset is undefined'
416         );
417     }
418
419     var callArgs = {
420         id: this._id,
421         datasetId: args.dataset._id
422     };
423
424     var result = native_.callSync('MLTrainerModelSetDataset', callArgs);
425
426     if (native_.isFailure(result)) {
427         throw native_.getErrorObjectAndValidate(
428             result,
429             ValidBasicExceptions,
430             AbortError
431         );
432     }
433 };
434
435 Model.prototype.setOptimizer = function() {
436     var args = validator_.validateArgs(arguments, [
437         {
438             name: 'optimizer',
439             type: types_.PLATFORM_OBJECT,
440             values: Optimizer
441         }
442     ]);
443
444     if (!args.has.optimizer) {
445         throw new WebAPIException(
446             WebAPIException.TYPE_MISMATCH_ERR, 'Invalid parameter: optimizer is undefined'
447         );
448     }
449
450     var callArgs = {
451         id: this._id,
452         optimizerId: args.optimizer._id
453     };
454
455     var result = native_.callSync('MLTrainerModelSetOptimizer', callArgs);
456
457     if (native_.isFailure(result)) {
458         throw native_.getErrorObjectAndValidate(
459             result,
460             ValidBasicExceptions,
461             AbortError
462         );
463     }
464 };
465
466 var ValidCreateLayerExceptions = ['NotSupportedError', 'TypeMismatchError', 'AbortError'];
467
468 var NO_ID = -1;
469 MachineLearningTrainer.prototype.createLayer = function() {
470     var args = validator_.validateArgs(arguments, [
471         {
472             name: 'type',
473             type: types_.ENUM,
474             values: Object.values(LayerType),
475             optional: false
476         }
477     ]);
478
479     var nativeArgs = {
480         type: args.type
481     };
482
483     var result = native_.callSync('MLTrainerLayerCreate', nativeArgs);
484     if (native_.isFailure(result)) {
485         throw native_.getErrorObjectAndValidate(
486             result,
487             ValidCreateLayerExceptions,
488             AbortError
489         );
490     }
491
492     return new Layer(result.id, args.type);
493 };
494
495 function ValidateAndReturnDatasetPaths(train, valid, test) {
496     try {
497         var args = {
498             train: tizen.filesystem.toURI(train),
499             valid: valid ? tizen.filesystem.toURI(valid) : '',
500             test: test ? tizen.filesystem.toURI(test) : ''
501         };
502         return args;
503     } catch (e) {
504         throw new WebAPIException(WebAPIException.NOT_FOUND_ERR, 'Path is invalid');
505     }
506 }
507
508 MachineLearningTrainer.prototype.createFileDataset = function() {
509     var args = validator_.validateArgs(arguments, [
510         {
511             name: 'train',
512             type: types_.STRING
513         },
514         {
515             name: 'valid',
516             type: types_.STRING,
517             optional: true,
518             nullable: true
519         },
520         {
521             name: 'test',
522             type: types_.STRING,
523             optional: false,
524             nullable: true
525         }
526     ]);
527     if (!args.has.train) {
528         throw new WebAPIException(
529             WebAPIException.TYPE_MISMATCH_ERR,
530             'Invalid parameter: training set path is undefined'
531         );
532     }
533
534     var nativeArgs = ValidateAndReturnDatasetPaths(
535         args.train,
536         args.has.valid ? args.valid : undefined,
537         args.has.test ? args.test : undefined
538     );
539
540     var result = native_.callSync('MLTrainerDatasetCreateFromFile', nativeArgs);
541     if (native_.isFailure(result)) {
542         throw native_.getErrorObjectAndValidate(
543             result,
544             ValidCreateLayerExceptions,
545             AbortError
546         );
547     }
548
549     return new Dataset(result.id, 'DATASET_FILE');
550 };
551
552 MachineLearningTrainer.prototype.createGeneratorDataset = function() {
553     var args = validator_.validateArgs(arguments, [
554         {
555             name: 'train',
556             type: types_.FUNCTION
557         },
558         {
559             name: 'validate',
560             type: types_.FUNCTION,
561             optional: true,
562             nullable: true
563         },
564         {
565             name: 'test',
566             type: types_.FUNCTION,
567             optional: true,
568             nullable: true
569         }
570     ]);
571     ValidateDatasetPaths(
572         args.train,
573         args.has.validate ? args.validate : undefined,
574         args.has.test ? args.test : undefined
575     );
576
577     // TODO
578     return new Dataset(result.id, 'DATASET_GENERATOR');
579 };
580
581 var ValidCreateOptimizerExceptions = [
582     'NotSupportedError',
583     'TypeMismatchError',
584     'AbortError'
585 ];
586
587 MachineLearningTrainer.prototype.createOptimizer = function() {
588     var args = validator_.validateArgs(arguments, [
589         {
590             name: 'type',
591             type: types_.ENUM,
592             values: Object.values(OptimizerType),
593             optional: false
594         }
595     ]);
596
597     var nativeArgs = {
598         type: args.type
599     };
600
601     var result = native_.callSync('MLTrainerOptimizerCreate', nativeArgs);
602     if (native_.isFailure(result)) {
603         throw native_.getErrorObjectAndValidate(
604             result,
605             ValidCreateOptimizerExceptions,
606             AbortError
607         );
608     }
609
610     return new Optimizer(result.id, args.type);
611 };
612
613 var ValidCreateModelWithConfigurationExceptions = [
614     'InvalidValuesError',
615     'NotFoundError',
616     'SecurityError',
617     'AbortError'
618 ];
619
620 MachineLearningTrainer.prototype.createModelWithConfiguration = function() {
621     var args = validator_.validateArgs(arguments, [
622         {
623             name: 'configPath',
624             type: types_.STRING,
625             optional: true
626         }
627     ]);
628     if (args.has.configPath) {
629         try {
630             args.configPath = tizen.filesystem.toURI(args.configPath);
631         } catch (e) {
632             throw new WebAPIException(WebAPIException.NOT_FOUND_ERR, 'Path is invalid');
633         }
634     }
635     var nativeArgs = {
636         configPath: args.configPath
637     };
638
639     var result = native_.callSync('MLTrainerModelCreate', nativeArgs);
640     if (native_.isFailure(result)) {
641         throw native_.getErrorObjectAndValidate(
642             result,
643             ValidCreateModelWithConfigurationExceptions,
644             AbortError
645         );
646     }
647
648     return new Model(result.id);
649 };
650
651 MachineLearningTrainer.prototype.createModel = function() {
652     var result = native_.callSync('MLTrainerModelCreate', {});
653     if (native_.isFailure(result)) {
654         throw new WebAPIException(WebAPIException.AbortError, 'Could not create model');
655     }
656
657     return new Model(result.id);
658 };