1282 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1282 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  **      ==============================
 | |
|  **       O           O      O   OOOO
 | |
|  **       O           O     O O  O   O
 | |
|  **       O           O     O O  O   O
 | |
|  **       OOOO   OOOO O     OOO  OOOO
 | |
|  **       O   O       O    O   O O   O
 | |
|  **       O   O       O    O   O O   O
 | |
|  **       OOOO        OOOO O   O OOOO
 | |
|  **      ==============================
 | |
|  **      Dr. Stefan Bosse http://www.bsslab.de
 | |
|  **
 | |
|  **      COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
 | |
|  **                 BY THE AUTHOR(S).
 | |
|  **                 THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
 | |
|  **                 MODIFIED, OR OTHERWISE USED IN A CONTEXT
 | |
|  **                 OUTSIDE OF THE SOFTWARE SYSTEM.
 | |
|  **
 | |
|  **    $AUTHORS:     Stefan Bosse
 | |
|  **    $INITIAL:     (C) 2006-2020 BSSLAB
 | |
|  **    $CREATED:     8-2-16 by sbosse.
 | |
|  **    $VERSION:     1.16.2
 | |
|  **
 | |
|  **    $INFO:
 | |
|  **
 | |
|  **  JavaScript AIOS Machine Learning API
 | |
|  **
 | |
|  ** type algorithm = {'dti','dt','id3','c45','kmeans','knn','knn2','mlp','slp','rl','svm','txt','cnn'}
 | |
|  **
 | |
|  **
 | |
|  ** id3: Symbolic Decision Tree algorithm
 | |
|  ** -------------------------------------
 | |
|  **
 | |
|  ** typeof @options = {
 | |
|  **   algorithm='id3',
 | |
|  **   data:{x1:number,x2:number,..,y:*} []
 | |
|  **   target:string is e.g. 'y'
 | |
|  **   features: string [] is e.g. ['x1','x2',..]
 | |
|  ** }
 | |
|  **
 | |
|  ** ice: decision tree algorithm supporting numbers with eps intervals (hybrid C45/ID3)
 | |
|  ** -------------------------------------
 | |
|  **
 | |
|  ** General feature variable set:
 | |
|  **
 | |
|  ** typeof @options = {
 | |
|  **   algorithm='dt',
 | |
|  **   data:{x1:number,x2:number,..,y:*} [],
 | |
|  **   target:string is e.g. 'y',
 | |
|  **   features: string [] is e.g. ['x1','x2',..],
 | |
|  **   eps:number is e.g. '5',
 | |
|  ** }
 | |
|  ** 
 | |
|  ** dti: interval decision tree algorithm
 | |
|  ** -------------------------------------
 | |
|  **
 | |
|  ** General feature variable set:
 | |
|  **
 | |
|  ** typeof @options = {
 | |
|  **   algorithm='dti',
 | |
|  **   data:{x1:number,x2:number,..,y:*} []
 | |
|  **   target:string is e.g. 'y'
 | |
|  **   features: string [] is e.g. ['x1','x2',..]
 | |
|  **   eps:number is e.g. '5',
 | |
|  **   maxdepth:number,
 | |
|  ** }
 | |
|  ** 
 | |
|  ** Or vector feature variables (i.e., features=[0,1,2,...n-1], target=n):
 | |
|  **
 | |
|  ** typeof @options = {
 | |
|  **   algorithm='dti',
 | |
|  **   x:* [] [],
 | |
|  **   y:* [],
 | |
|  **   eps:number is e.g. '5',
 | |
|  **   maxdepth:number,
 | |
|  ** }
 | |
|  **
 | |
|  ** knn: k-Nearest-Neighbour Algorithm
 | |
|  ** ----------------------------------
 | |
|  **
 | |
|  ** typeof @options = {
 | |
|  **   algorithm='knn',
 | |
|  **   x: number [][], 
 | |
|  **   y: * []
 | |
|  ** }
 | |
|  **
 | |
|  ** mlp: multi layer perceptron Algorithm
 | |
|  ** ----------------------------------
 | |
|  **
 | |
|  ** typeof @options = {
 | |
|  **   algorithm='mlp',
 | |
|  **   x: number [][], 
 | |
|  **   y: number [] [] | * [],
 | |
|  **   hidden_layers?:number [],
 | |
|  **   lr?:number,
 | |
|  **   epochs?:number,
 | |
|  **   labels?:string [], 
 | |
|  **   features?: string [], 
 | |
|  **   normalize?, 
 | |
|  **   verbose?:number
 | |
|  ** }
 | |
|  **
 | |
|  **
 | |
|  ** cnn: Convolutional Neural Network for numerial (2D) data
 | |
|  ** -------------------------------------
 | |
|  **
 | |
|  ** General feature variable set:
 | |
|  **
 | |
|  ** typeof @options = {
 | |
|  **   algorithm='cnn',
 | |
|  **   data:{x:[]|[][],y:'a} []
 | |
|  **   layers: layer [],
 | |
|  **   trainer:trainer,
 | |
|  ** }
 | |
|  ** type layer = 
 | |
|  **  {type:'input', out_sx:number, out_sy:number, out_depth:number} | // Input Layer
 | |
|  **  {type:'conv', sx:number, filters:number, stride:number, pad:number, activation:string} | // Convolution Layer
 | |
|  **  {type:'pool', sx:number, stride:number} | // Pooling Layer
 | |
|  **  {type:'softmax', num_classes:number} | // Classifier Layers
 | |
|  **  {type:'svm', num_classes:number| // Classifier Layers
 | |
|  **  {type:'fc', num_neurons:number, activation:string} // Fully Connected Layer
 | |
|  **
 | |
|  ** typeof activation = 'relu'| 'maxout' | 'sigmoid' | 'tanh' ..
 | |
|  **
 | |
|  ** type trainer = 
 | |
|  **  {method: 'sgd', learning_rate:number,  momentum: number, batch_size:number, l2_decay:number} |
 | |
|  **  {method: 'adadelta', learning_rate:number,  eps: number, ro:number, batch_size:number, l2_decay:number} |
 | |
|  **  {method: 'adam', learning_rate:number, eps: number, beta1: number, beta2: number, batch_size: number, l2_decay:number} |
 | |
|  **  ..
 | |
|  **
 | |
|  ** text: text analysis (similarity checking)
 | |
|  ** -----------------------------------------
 | |
|  **   classify(model,string) -> {match:number [0..1],string:string }
 | |
|  **   learn({algorithm:ML.TXT, data:string []]) -> model
 | |
|  **   test({algorithm:ML.TXT,string:string}|model,string) -> number [0..1]
 | |
|  **   similarity(string,string) -> number [0..1]
 | |
|  ** 
 | |
|  **    $ENDOFINFO
 | |
|  */
 | |
| var Io = Require('com/io');
 | |
| var Comp = Require('com/compat');
 | |
| 
 | |
| 
 | |
| var ICE = Require('ml/ice'); // ICE ID3/C45 eps
 | |
| var DTI = Require('ml/dti');
 | |
| var KNN = Require('ml/knn');
 | |
| var KMN = Require('ml/kmeans');
 | |
| var SVM = Require('ml/svm');
 | |
| var MLP = Require('ml/mlp');
 | |
| var ID3 = Require('ml/id3');
 | |
| var C45 = Require('ml/C45');
 | |
| var TXT = Require('ml/text');
 | |
| var RF  = Require('ml/rf');
 | |
| var RL  = Require('ml/rl');
 | |
| var STAT= Require('ml/stats');
 | |
| var CNN = Require('ml/cnn');
 | |
| var ANN = Require('ml/ann');
 | |
| var PCA = Require('ml/pca');
 | |
| 
 | |
| var current=none;
 | |
| var Aios=none;
 | |
| 
 | |
| var options = {
 | |
|   version: '1.16.2'
 | |
| }
 | |
| 
 | |
| // Some definitions
 | |
| var ML = {
 | |
|   // Algorithms
 | |
|   ANN:'ann',    // neataptic NN 
 | |
|   C45:'c45',
 | |
|   CNN:'cnn',
 | |
|   ICE:'ice',   // ICE ID3/C45 eps
 | |
|   DTI:'dti',
 | |
|   ID3:'id3',
 | |
|   KMN:'kmeans',
 | |
|   KNN:'knn',
 | |
|   KNN2:'knn2',
 | |
|   MLP:'mlp',
 | |
|   RF:'rf',    // Random Forest
 | |
|   RL:'rl',    // Reinforcement Leerner
 | |
|   SLP:'slp',  // Synonym for MLP (but single layer)
 | |
|   SVM:'svm',
 | |
|   TXT:'txt',
 | |
|   // Some Functions
 | |
|   EUCL:'euclidean',
 | |
|   PEAR:'pearson',
 | |
|   
 | |
|   // RL agents
 | |
|   DPAgent:'DPAgent',
 | |
|   TDAgent:'TDAgent',
 | |
|   DQNAgent:'DQNAgent',
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Computes Log with base-2
 | |
|  * @private
 | |
|  */
 | |
| function log2(n) {
 | |
|   return Math.log(n) / Math.log(2);
 | |
| }
 | |
| 
 | |
| function obj2Array(row,features) {
 | |
|   return features.map(function (attr) { return row[attr] });
 | |
| }
 | |
| function objSlice(row,features) {
 | |
|   var o = {};
 | |
|   features.forEach(function (attr) { o[attr]=row[attr] });
 | |
|   return o;
 | |
| }
 | |
| 
 | |
| // transform [v][] -> v[]
 | |
| function relax(mat) {
 | |
|   if (Comp.obj.isMatrix(mat) && mat[0].length==1) return mat.map(function (row) { return row[0]})
 | |
|   else return mat;
 | |
| }
 | |
| 
 | |
| // transform v[] -> [v][]
 | |
| function wrap(mat) {
 | |
|   if (!Comp.obj.isMatrix(mat)) return mat.map(function (v) { return [v]})
 | |
|   else return mat
 | |
| }
 | |
| 
 | |
| /* Common data transformation between different formats
 | |
| **
 | |
| ** 1a. need='xy':   data={$x:'a,$y:'b}[] -> {x:{$x} [], y:'b[]}
 | |
| ** 1b. need='xy':   data=('a|'b)[][] -> {x:'a [][], y:'b[]}
 | |
| ** 1c. need='xry':  data=('a|'b)[][] -> {x:{$x} [], y:'b[]}
 | |
| ** 1c. need='io':   data=number[][] -> {input:number, output:number} []
 | |
| ** 1d. need='io':   data={$x:number,$y:number}[] -> {input:number, output:number} []
 | |
| ** 2. need='xmy':   data={$x:'a,$y:'b}[] -> {x:'a [][], y:'b[]}
 | |
| ** 3. need='d':     data={x:'a[][],y:'b[]}} -> {data:{$x:'a,$y:'b}[][]}
 | |
| ** 4. need='dm':    data={x:'a[][],y;'b[]} -> {data:('a|'b)[][]}
 | |
| ** 5. need='m':     data={$x:'a}[] -> 'a [][]
 | |
| ** 6. need='a':     data={$x:'a} -> 'a []
 | |
| 
 | |
| ** typeof options = {
 | |
| **   scale:   {k:number, off:number, shift:number} is transformation of input data,
 | |
| **   xscale:  {k:number, off:number, shift:number} is transformation of input data,
 | |
| **   yscale:  {k:number, off:number, shift:number} is transformation of output data,
 | |
| **   features : string [] is feature variable list,
 | |
| **   target: string is output variable,
 | |
| **
 | |
| **/
 | |
| function scale(vrow,scala) {
 | |
|   if (!scala) return vrow;
 | |
|   if (typeof vrow == 'number') {
 | |
|     if (typeof scala.k == 'number')
 | |
|       return scala.shift+(vrow-scala.off)*scala.k
 | |
|     else
 | |
|       return scala.shift+(vrow-scala.off[0])*scala.k[0];
 | |
|   }
 | |
|   if (typeof scala.k == 'number')
 | |
|     return vrow.map(function (col,i) { 
 | |
|       return scala.shift+(col-scala.off)*scala.k })
 | |
|   else
 | |
|     return vrow.map(function (col,i) { 
 | |
|       return scala.shift+(col-scala.off[i])*scala.k[i] })
 | |
| }
 | |
| 
 | |
| function unscale(vrow,scala) {
 | |
|   if (!scala) return vrow;
 | |
|   if (typeof vrow == 'number') {
 | |
|     if (typeof scala.k == 'number')
 | |
|       return (vrow-scala.shift)/scala.k+scala.off
 | |
|     else
 | |
|       return (vrow-scala.shift)/scala.k[0]+scala.off[0]
 | |
|   }
 | |
| }
 | |
| 
 | |
| function preprocess(data,need,options) {
 | |
|   var row,x,y,_data;
 | |
|   options=options||{};
 | |
|   var scala=options.scale || options.xscale;
 | |
|   function array(data) {
 | |
|     return Comp.obj.isArray(data)?data:[data]
 | |
|   } 
 | |
|   if (Comp.obj.isArray(data)) {
 | |
|     row=data[0];
 | |
|     switch (need) {
 | |
|       case 'xy':
 | |
|       case 'xry':
 | |
|         if (options.target!=undefined && options.features!=undefined) {
 | |
|           if (Comp.obj.isArray(row) && need=='xy') {
 | |
|             if (Number(options.target)==row.length-1) {
 | |
|               x=data.map(function (row) { return scale(row.slice(0,options.target),scala) });
 | |
|               y=data.map(function (row) { return row[options.target] })
 | |
|             }
 | |
|           } else  if (Comp.obj.isObj(row)) {
 | |
|             if (typeof options.target == 'string') {
 | |
|               x=data.map(function (row) { return scale(objSlice(row,options.features),scala) });
 | |
|               y=data.map(function (row) { return row[options.target] });
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         if (x && y) return {x:x,y:y}
 | |
|         break;
 | |
|       case 'a':
 | |
|         if (Comp.obj.isArray(data) && typeof data[0] != 'object') return {data:data};  
 | |
|         if (Comp.obj.isObject(data) && options.features!=undefined) {
 | |
|           return { data:data.map(function (row) { 
 | |
|                     return scale(objSlice(row,options.features),scala) })};
 | |
|         }
 | |
|         break;
 | |
|       case 'm':
 | |
|        if (Comp.obj.isMatrix(data)) return {data:data};
 | |
|         if (Comp.obj.isObject(row) && options.features!=undefined) {
 | |
|           return { data:data.map(function (row) { 
 | |
|                     return scale(obj2Array(row,options.features),scala) })};
 | |
|         }
 | |
|        break;  
 | |
|       case 'xmy':
 | |
|         if (Comp.obj.isObject(row) && options.features!=undefined && options.target!=undefined) {
 | |
|           return { x:data.map(function (row) { 
 | |
|                       return scale(obj2Array(row,options.features),scala) }),
 | |
|                    y:data.map(function (row) { return row[options.target]})};
 | |
|         }
 | |
|        break;  
 | |
|       case 'io':
 | |
|         if (Comp.obj.isArray(row) && options.target!=undefined) {
 | |
|           // number [][] 
 | |
|           if (Number(options.target)==row.length-1) {
 | |
|             _data=data.map(function (row) { return { input:scale(row.slice(0,options.target),scala),
 | |
|                                                      output:array(row[options.target]) }});
 | |
|             return _data
 | |
|           } 
 | |
|         } else if (Comp.obj.isObject(row) && options.target!=undefined && options.features!=undefined) {
 | |
|           _data=data.map(function (row) { return { input:scale(obj2Array(row,options.features),scala),
 | |
|                                                    output:array(row[options.target]) }});
 | |
|           return _data
 | |
|         }
 | |
| 
 | |
|         break;
 | |
|     }
 | |
|   } else if (data.x && data.y) {
 | |
|     if (Comp.obj.isArray(data.x) && Comp.obj.isArray(data.y)) {
 | |
|       row=data.x[0];
 | |
|       switch (need) {
 | |
|         case 'io':
 | |
|         if (Comp.obj.isArray(row)) {
 | |
|           // number [][] 
 | |
|           _data=data.x.map(function (row, rowi) { return { input:scale(row,scala),
 | |
|                                                            output:array(data.y[rowi]) }});
 | |
|           return _data          
 | |
|         } 
 | |
|         if (Comp.obj.isObject(row) && options.features!=undefined) {
 | |
|           _data=data.x.map(function (row, rowi) { return { input:scale(obj2Array(row,options.features),scala),
 | |
|                                                            output:array(data.y[rowi]) }});
 | |
|           return _data          
 | |
|         }
 | |
|         break;
 | |
|         case 'xm':
 | |
|           if (Comp.obj.isArray(row)) return data.x;
 | |
|           break;
 | |
|         case 'xmy':
 | |
|           if (Comp.obj.isArray(row)) return { x:data.x, y:data.y};
 | |
|           break;
 | |
|         case 'xmya':
 | |
|           if (Comp.obj.isArray(row)) return { x:data.x, y:data.y.map(array)};
 | |
|           break;
 | |
|         case 'd':
 | |
|           return data.x.map(function (row,rowi) {
 | |
|             var newrow={};
 | |
|             if (options.features && options.target) {
 | |
|               options.features.forEach(function (f,coli) {
 | |
|                 newrow[f]=row[coli];
 | |
|               });
 | |
|               newrow[options.target]=data.y[rowi];
 | |
|             } else {
 | |
|               row.forEach(function (col,f) {
 | |
|                 newrow[String(f)]=col;                
 | |
|               });
 | |
|               newrow[String(row.length)]=data.y[rowi];
 | |
|             }
 | |
|             return newrow;
 | |
|           })
 | |
|           break;
 | |
|       } 
 | |
|     }   
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| // Agent AIOS API
 | |
| var  ml = {
 | |
|   // only RL
 | |
|   action : function (model,arg) {
 | |
|     switch (model.algorithm) {
 | |
|       // Selects and returns next action from set of actions
 | |
|       case ML.RL:
 | |
|         switch (model.kind) {
 | |
|           case ML.DQNAgent:
 | |
|             // arg == state array
 | |
|             return model.actions[RL.DQNAgent.code.act(model,arg)];   
 | |
|             break;
 | |
|           case ML.DPAgent:
 | |
|             // arg == state (integer number)
 | |
|             return model.actions[RL.DPAgent.code.act(model,arg)];   
 | |
|             break;
 | |
|           case ML.TDAgent:
 | |
|             // arg == state (integer number)
 | |
|             return model.actions[RL.TDAgent.code.act(model,arg)];   
 | |
|             break;
 | |
|         }
 | |
|         break;   
 | |
|     }
 | |
|   },
 | |
|   /** Classification (prediction): Apply sample data to learned model.
 | |
|    *  Returns prediction result.
 | |
|    *
 | |
|    */ 
 | |
|   classify: function (model,samples) {
 | |
|     var x,solutions,result;
 | |
|     switch (model.algorithm) {
 | |
|     
 | |
|       case ML.ANN:
 | |
|         if (Comp.obj.isArray(samples)) 
 | |
|           return samples.map(function (sample) { 
 | |
|             return model.network.activate(sample) 
 | |
|           });
 | |
|         else
 | |
|           return model.network.activate(samples);
 | |
| 
 | |
|       case ML.CNN:
 | |
|         if (Comp.obj.isMatrix(samples))
 | |
|           return samples.map(function (sample) {
 | |
|             return CNN.predict(model,sample);
 | |
|           });
 | |
|         else
 | |
|           return CNN.predict(model,samples);
 | |
|         break;
 | |
| 
 | |
|       case ML.C45:
 | |
|         // Sample row format: [x1,x2,..,xn]
 | |
|         if (Comp.obj.isMatrix(samples)) {
 | |
|           return samples.map(function (sample) {
 | |
|             return C45.classify(model,sample);
 | |
|           });
 | |
|         } else if (Comp.obj.isArray(samples) && !Comp.obj.isObj(samples[0])) {
 | |
|           return C45.classify(model,samples);
 | |
|         } else if (Comp.obj.isArray(samples) &&  Comp.obj.isObj(samples[0])) {
 | |
|           return samples.map(function (sample) {
 | |
|             return C45.classify(model,sample); 
 | |
|           });
 | |
|         } else if (Comp.obj.isObj(samples)) {
 | |
|           return C45.classify(model,samples);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       case ML.DT:
 | |
|       case ML.ICE:
 | |
|         if (Comp.obj.isMatrix(samples) ||
 | |
|             Comp.obj.isArray(samples) && Comp.obj.isObj(samples[0])) 
 | |
|           return samples.map(function (sample) { 
 | |
|             return ICE.predict(model,sample) 
 | |
|           });
 | |
|         else 
 | |
|           return ICE.predict(model,samples);
 | |
| 
 | |
|       case ML.DTI:
 | |
|         if (Comp.obj.isMatrix(samples)) 
 | |
|           return samples.map(function (sample) { 
 | |
|             return DTI.predict(model,sample) 
 | |
|           });
 | |
|         else
 | |
|           return DTI.predict(model,samples);
 | |
| 
 | |
|       case ML.ID3:
 | |
|         if (Comp.obj.isArray(samples)) 
 | |
|           return samples.map(function (sample) { 
 | |
|             return ID3.predict(model,sample) 
 | |
|           });
 | |
|         else
 | |
|           return ID3.predict(model,samples);
 | |
| 
 | |
|       case ML.KNN:
 | |
|         if (Comp.obj.isMatrix(samples))
 | |
|           return KNN.predict(model,samples);        
 | |
|         else if (Comp.obj.isArray(samples) && Comp.obj.isObj(samples[0]))
 | |
|           return KNN.predict(model,samples.map(function (sample) { 
 | |
|             return obj2Array(sample,model.features)}));
 | |
|         else if (Comp.obj.isObj(samples))
 | |
|           return KNN.predict(model,obj2Array(samples,model.features));
 | |
|         else
 | |
|           return KNN.predict(model,samples);
 | |
|         break;
 | |
| 
 | |
|       case ML.KNN2:
 | |
|         if (Comp.obj.isMatrix(samples))
 | |
|           return samples.map(function (sample) {
 | |
|             return KNN.predict2(model,sample);
 | |
|           });
 | |
|         else if (Comp.obj.isArray(samples) && Comp.obj.isObj(samples[0]))
 | |
|           return samples.map(function (sample) {
 | |
|              return KNN.predict2(model,obj2Array(sample,model.features))
 | |
|             })
 | |
|         else if (Comp.obj.isObj(samples))
 | |
|           return KNN.predict2(model,obj2Array(samples,model.features));
 | |
|         else
 | |
|           return KNN.predict2(model,samples);
 | |
|         break;
 | |
| 
 | |
|       case ML.KMN:
 | |
|         return model.clusters
 | |
|         break;
 | |
| 
 | |
|       case ML.RF:
 | |
|         if (model.labels) {
 | |
|           if (Comp.obj.isMatrix(samples)) {
 | |
|             return samples.map(function (sample) {
 | |
|               return model.rfs.map(function (rf) {
 | |
|                 return RF.code.predictOne(rf,sample);
 | |
|               }).map(function (v,i) {
 | |
|                 return { value:model.labels[i], prob:v }
 | |
|               })
 | |
|             });
 | |
|           } else if (Comp.obj.isArray(samples) && typeof samples[0] == 'number') {
 | |
|             return model.rfs.map(function (rf) {
 | |
|               return RF.code.predictOne(rf,samples);
 | |
|             }).map(function (v,i) {
 | |
|                 return { value:model.labels[i], prob:v }
 | |
|             })
 | |
|           } // TODO
 | |
|         } else {
 | |
|           // Sample row format: [x1,x2,..,xn]
 | |
|           if (Comp.obj.isMatrix(samples)) {
 | |
|             return samples.map(function (sample) {
 | |
|               return RF.code.predictOne(model,sample);
 | |
|             });
 | |
|           } else if (Comp.obj.isArray(samples) && typeof samples[0] == 'number') {
 | |
|             return RF.predictOne(model,samples);
 | |
|           } // TODO
 | |
|         }
 | |
|         // preprocess(samples,'m')
 | |
|         break;
 | |
|                 
 | |
|       case ML.SVM:
 | |
|         if (!model._labels) {
 | |
|           // Single SVM 
 | |
|           if (Comp.obj.isMatrix(samples))
 | |
|             return samples.map(function (sample) {
 | |
|               return SVM.code.predict(model,sample);
 | |
|             });
 | |
|           else
 | |
|             return SVM.code.predict(model,samples);
 | |
|         } else {
 | |
|           // Multi SVM
 | |
|           if (Comp.obj.isMatrix(samples))
 | |
|             return samples.map(function (sample) {
 | |
|               solutions=model.svms.map(function (svm,index) { 
 | |
|                 if (svm.threshold==false)
 | |
|                   return SVM.code.predict(svm,sample)
 | |
|                 else
 | |
|                   return SVM.code.predict(svm,sample); 
 | |
|               });
 | |
|               return solutions.map(function (v,i) { return { value:model._labels[i], prob:v } });
 | |
|             });
 | |
|           else {
 | |
|             solutions=model.svms.map(function (svm,index) { 
 | |
|                 if (svm.threshold==false)
 | |
|                   return SVM.code.predict(svm,samples)
 | |
|                 else
 | |
|                   return SVM.code.predict(svm,samples)==1; 
 | |
|             })
 | |
|             return solutions.map(function (v,i) { return { value:model._labels[i], prob:v } });
 | |
|           }
 | |
|         }
 | |
|         break;
 | |
|         
 | |
|       case ML.SLP:
 | |
|       case ML.MLP:
 | |
|         if (Comp.obj.isMatrix(samples)) {
 | |
|           x=samples;          
 | |
|           if (model.xscale) 
 | |
|             x=x.map(function (row) { return scale(row,model.xscale) });
 | |
|           result = model.labels?MLP.code.predict(model,x).map(function (r) {
 | |
|             var o={};
 | |
|             r.forEach(function (v,i) { o[model.labels[i]]=v });
 | |
|             return o;
 | |
|           }):relax(MLP.code.predict(model,x));
 | |
|         } else if (Comp.obj.isArray(samples)) {
 | |
|           x=samples;
 | |
|           if (model.xscale) 
 | |
|             x=scale(x,model.xscale);
 | |
|           result = model.labels?MLP.code.predict(model,[x]).map(function (r) {
 | |
|             var o={};
 | |
|             r.forEach(function (v,i) { o[model.labels[i]]=v });
 | |
|             return o;
 | |
|           })[0]:relax(MLP.code.predict(model,[x])[0]);
 | |
|         } else if (Comp.obj.isObj(samples) && model.features) {
 | |
|           x=model.features.map(function (f) { return samples[f] });
 | |
|           if (model.xscale) 
 | |
|             x=scale(x,model.xscale);
 | |
|           result = model.labels?MLP.code.predict(model,[x]).map(function (r) {
 | |
|             var o={};
 | |
|             r.forEach(function (v,i) { o[model.labels[i]]=v });
 | |
|             return o;
 | |
|           })[0]:relax(MLP.code.predict(model,[x])[0]); 
 | |
|         }
 | |
|         if (Comp.obj.isArray(result)) {
 | |
|           return model.yscale?result.map(function (y) { return unscale(y,model.yscale) }):result;
 | |
|         } else {
 | |
|         
 | |
|         }
 | |
|         break;
 | |
|         
 | |
|        case ML.TXT:
 | |
|         // typeof options = {data: string []}
 | |
|         if (Comp.obj.isArray(samples))
 | |
|           return samples.map(function (sample) { return TXT.classify(model,sample) });
 | |
|         else
 | |
|           return TXT.classify(model,samples);
 | |
|         break;
 | |
| 
 | |
|    }
 | |
|   },
 | |
|   
 | |
|   compact: function (model) {
 | |
|     switch (model.algorithm) {
 | |
|       case ML.DTI:
 | |
|       default:
 | |
|         return DTI.compactTree(model);
 | |
|     }
 | |
|   },
 | |
|   
 | |
|   depth: function (model) {
 | |
|     switch (model.algorithm) {
 | |
|       case ML.DTI:
 | |
|         return DTI.depth(model);
 | |
|       case ML.DT:
 | |
|       case ML.ICE:
 | |
|         return ICE.depth(model);
 | |
|       case ML.C45:
 | |
|         return C45.depth(model);
 | |
|       case ML.ID3:
 | |
|         return ID3.depth(model);
 | |
|     }
 | |
|   },
 | |
|   
 | |
|   
 | |
|   evaluate: function (model,target,samples) {
 | |
|     switch (model.algorithm) {
 | |
|       case ML.DTI:
 | |
|       default:
 | |
|         return DTI.evaluate(model,target,samples);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   info: function (model) {
 | |
|     switch (model.algorithm) {
 | |
|       case ML.C45:
 | |
|         return C45.info(model);
 | |
|       case ML.DT:
 | |
|       case ML.ICE:
 | |
|         return ICE.info(model);
 | |
|       case ML.ID3:
 | |
|         return ID3.info(model);
 | |
|     }
 | |
|   },
 | |
|   /** Learning: Create a classification model from training data (or an empty model that can be updated)
 | |
|    *
 | |
|    */
 | |
|   learn: function (options) {
 | |
|     var model,data,data2,x,y,features,featureTypes,test,target,
 | |
|         result,cols,n_ins,n_outs,x,y,xscale,xoffset,xshift,yscale,yoffset,yshift,key,err,
 | |
|         t0=Io.time();
 | |
|     if (options==_) options={};
 | |
|     switch (options.algorithm) {
 | |
|     
 | |
|       case ML.ANN:
 | |
|         // typeof options = { x,y,features?,target?,layers:number [], trainerror:number}
 | |
|         data = preprocess(options,'io',options);
 | |
|         model={};
 | |
|         model.algorithm=options.algorithm
 | |
|         if (!options.layers) options.layers=[]
 | |
|         if (data)
 | |
|           model.network = new ANN.Network(options.layers[0],options.layers[options.layers.length-1]);
 | |
|         else throw 'ML.learn.ANN: Invalid options';
 | |
|         model.network.evolve(data,options);
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;      
 | |
|         
 | |
| 
 | |
|       case ML.CNN:
 | |
|         // typeof options = {x:[][],y:[],..}
 | |
|         model = CNN.create(options);
 | |
|         model.algorithm=options.algorithm;
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;
 | |
| 
 | |
|       case ML.C45:
 | |
|         // typeof options = {data: {}[], target:string, features: string []} |
 | |
|         //                  {data: [][], target?:string, features?: string []} |
 | |
|         //                  {x: number [][], y:[]} |
 | |
|         //                  {data: {x,y}[] }
 | |
|         var model = C45.create();
 | |
|         if (options.x && options.y) {
 | |
|           features=options.x[0].map(function (col,i) { return String(i) }); 
 | |
|           featureTypes=options.x[0].map(function (col,i) { return 'number' });
 | |
|           data=options.x.map(function (row,i) { row=row.slice(); row.push(options.y[i]); return row});
 | |
|           target='y';
 | |
|         } else if (options.data && Comp.obj.isMatrix(options.data)) {
 | |
|           data=options.data;
 | |
|           features=options.features||options.data[0].slice(0,-1).map(function (col,i) { return String(i) });
 | |
|           featureTypes=options.data[0].slice(0,-1).map(function (col,i) { return typeof col == 'number'?'number':'category' });
 | |
|           target=options.target||'y';
 | |
|         } else if (options.data && Comp.obj.isObj(options.data[0]) && options.data[0].x && options.data[0].y!=undefined) {
 | |
|           data=options.data.map(function (row) { return row.x.concat(row.y) });
 | |
|           features=options.features||options.data[0].x.slice(0,-1).map(function (col,i) { return String(i) });
 | |
|           featureTypes=options.data[0].x.slice(0,-1).map(function (col,i) { return typeof col == 'number'?'number':'category' });
 | |
|           target=options.target||'y';
 | |
|         } else if (options.data && Comp.obj.isArray(options.data) && Comp.obj.isObj(options.data[0]) && 
 | |
|                    options.target && options.features) {
 | |
|           rowNames=Comp.obj.isArray(options.target)?options.features.concat(options.target):
 | |
|                                                     options.features.concat([options.target]);
 | |
|           data=options.data.map(function (row) { return obj2Array(row,rowNames) })
 | |
|           features=options.features;
 | |
|           featureTypes=data[0].slice(0,-1).map(function (col,i) { return typeof col == 'number'?'number':'category' });
 | |
|           target=options.target;
 | |
|         } else throw 'ML.learn.C45: Invalid options';
 | |
| 
 | |
|         C45.train(model,{
 | |
|           data: data,
 | |
|           target: target,
 | |
|           features: features,
 | |
|           featureTypes: featureTypes
 | |
|         });
 | |
|         model.algorithm=options.algorithm
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;
 | |
| 
 | |
| 
 | |
|       case ML.DTI:
 | |
|         // typeof options = {data: {}[], target:string, features: string [], eps;number, maxdepth} |
 | |
|         //                   {x: number [][], y:[], eps;number, maxdepth}
 | |
|         if (options.eps==_) options.eps=0;
 | |
|         if (options.maxdepth==_) options.maxdepth=20;
 | |
|         if (options.data && options.target && options.features)
 | |
|           model = DTI.create(options);
 | |
|         else if (options.x && options.y) {
 | |
|           if (options.x.length != options.y.length) throw 'ML.learn.DTI: X and Y vector have different length';
 | |
|           data=options.x.map(function (row,i) { row=row.slice(); row.push(options.y[i]); return row});
 | |
|           features=Comp.array.init(data[0].length-1,function (i) { return String(i)});
 | |
|           target=String(data[0].length-1);
 | |
|           // console.log(data,features,target)
 | |
|           model = DTI.create({
 | |
|             data:data,
 | |
|             features:features,
 | |
|             target:target,
 | |
|             eps:options.eps,
 | |
|             maxdepth:options.maxdepth
 | |
|           });
 | |
|         } else throw 'ML.learn.DTI: Invalid options';
 | |
|         model.algorithm=options.algorithm;
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
| 
 | |
| 
 | |
|       case ML.ICE:
 | |
|       case ML.DT:
 | |
|         if (options.eps==_) options.eps=0;
 | |
|         if (options.data && options.target && options.features)
 | |
|           model = ICE.create(options);                  
 | |
|         else if (options.x && options.y) {
 | |
|           if (options.x.length != options.y.length) throw 'ML.learn.ICE: X and Y vector have different length';
 | |
|           data=options.x.map(function (row,i) { row=row.slice(); row.push(options.y[i]); return row});
 | |
|           features=Comp.array.init(data[0].length-1,function (i) { return String(i)});
 | |
|           target=String(data[0].length-1);
 | |
|           model = ICE.create({
 | |
|             data:data,
 | |
|             features:features,
 | |
|             target:target,
 | |
|             eps:options.eps,
 | |
|           });
 | |
|         } else throw 'ML.learn.ICE: Invalid options';
 | |
|         model.algorithm=options.algorithm;
 | |
|         model.eps=options.eps;
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;      
 | |
| 
 | |
|       case ML.ID3:
 | |
|         if (options.data && options.target && options.features)
 | |
|           model = ID3.createTree(options.data,options.target,
 | |
|                                  options.features);
 | |
|         else throw 'ML.learn.ID3: Invalid options';
 | |
|         model.algorithm=options.algorithm
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;      
 | |
|           
 | |
|       case ML.KNN:
 | |
|         // typeof @options = {data: {}[]|[][], distance?:function|string,k?:number}
 | |
|         // typeof @options = {x:number [][], y:number [], 
 | |
|         //                    distance?:function|string,k?:number}
 | |
|         if (options.features && options.target) target=options.target,features = options.features;
 | |
|         else {
 | |
|           features = [];
 | |
|           if (options.data) {
 | |
|             for(key in options.data[0]) features.push(key);
 | |
|             target = features.pop()
 | |
|           } else if (options.x) {
 | |
|             for(key in options.x[0]) features.push('x'+key);
 | |
|             target='y';
 | |
|           }
 | |
|         }
 | |
|         if (options.data && Comp.obj.isObj(options.data[0])) {
 | |
|           x = options.data.map(function (row) { return obj2Array(row,features) });
 | |
|           y = options.data.map(function (row) { return row[target] })
 | |
|         } else if (options.data && Comp.obj.isMatrix(options.data)) {
 | |
|           x = options.data,map(function (row) { return row.slice(0,row.length-1) });
 | |
|           y = options.data,map(function (row) { return row[row.length-1] });
 | |
|         } else if (options.x && options.y) {
 | |
|           x = options.x;
 | |
|           y = options.y;
 | |
|         }
 | |
|         model = KNN.create(
 | |
|           x,
 | |
|           y,
 | |
|           {
 | |
|             distance:options.distance,
 | |
|             k:options.k
 | |
|           });
 | |
|         model.algorithm = options.algorithm
 | |
|         model.features  = features
 | |
|         model.target    = target
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;
 | |
| 
 | |
|       case ML.KNN2:
 | |
|         // typeof @options = {data: {}[]|[][], distance?:function|string,k?:number}
 | |
|         // typeof @options = {x:number [][], y:number [], 
 | |
|         //                    distance?:function|string,k?:number}
 | |
|         if (options.features && options.target) target=options.target,features = options.features;
 | |
|         else {
 | |
|           features = [];
 | |
|           if (options.data) {
 | |
|             for(key in options.data[0]) features.push(key);
 | |
|             target = features.pop()
 | |
|           } else if (options.x) {
 | |
|             for(key in options.x[0]) features.push('x'+key);
 | |
|             target='y';
 | |
|           }
 | |
|         }
 | |
|         if (options.data && Comp.obj.isObj(options.data[0])) {
 | |
|           x = options.data.map(function (row) { return obj2Array(row,features) });
 | |
|           y = options.data.map(function (row) { return row[target] })
 | |
|         } else if (options.data && Comp.obj.isMatrix(options.data)) {
 | |
|           x = options.data,map(function (row) { return row.slice(0,row.length-1) });
 | |
|           y = options.data,map(function (row) { return row[row.length-1] });
 | |
|         } else if (options.x && options.y) {
 | |
|           x = options.x;
 | |
|           y = options.y;
 | |
|         }
 | |
|         model = KNN.create2(
 | |
|           {
 | |
|             x : x,
 | |
|             y : y,
 | |
|             distance:options.distance,
 | |
|             k:options.k
 | |
|           });
 | |
|         model.algorithm=options.algorithm
 | |
|         model.features = features
 | |
|         model.target = target
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;
 | |
|         
 | |
|       case ML.KMN:
 | |
|         if (options.data && Comp.obj.isMatrix(options.data)) {
 | |
|           data=options.data;
 | |
|         } 
 | |
|         model = KMN.cluster({
 | |
|           data:data,
 | |
|           k:options.k,
 | |
|           distance:options.distance,
 | |
|           epochs:options.epochs,
 | |
|         })
 | |
|         model.algorithm=options.algorithm
 | |
|         model.data = data
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;
 | |
|                 
 | |
|       case ML.RF:
 | |
|         var model={};
 | |
|         // Single Binary RF (y={-1,1}) or Multi-RF (y:string is in labels)
 | |
|         // typeof options = {data: {}[], target:string, features: string []} |
 | |
|         //                  {data: [][], target?:string, features?: string []} |
 | |
|         //                  {x: number [][], y: {-1,1} []} |
 | |
|         //                  {data: {x,y}[] }
 | |
|         //                  {data: {x,y}[], labels: string [] }
 | |
|         if (!options.x || !options.y) throw 'ML.learn.RF: Invalid options';
 | |
|         // data=preprocess(data,'xmy',{features:features,target:target})
 | |
|         data={x:options.x,y:options.y}; // TODO 
 | |
|         if (options.labels) {
 | |
|           // multi-RF
 | |
|           model.labels = options.labels;
 | |
|           model.rfs = model.labels.map (function (label) { return RF() });
 | |
|           model.rfs.forEach (function (rf,i) {
 | |
|             var y = data.y.map(function (label) { return label==model.labels[i]?1:-1} );
 | |
|             RF.code.train(rf,options.x,y,{
 | |
|               numTrees:options.numTrees,
 | |
|               maxDepth:options.maxDepth,
 | |
|               numTries:options.numTries,
 | |
|               type:options.weakType,
 | |
|             });
 | |
|           });
 | |
|         } else {
 | |
|           model = RF();
 | |
|           features=options.x[0].map(function (col,i) { return String(i) }); 
 | |
|           target='y';
 | |
|         
 | |
|           RF.code.train(model,
 | |
|             options.x,
 | |
|             options.y,
 | |
|             {
 | |
|               numTrees:options.numTrees,
 | |
|               maxDepth:options.maxDepth,
 | |
|               numTries:options.numTries,
 | |
|               type:options.weakType,
 | |
|             });    
 | |
|         }
 | |
|         model.algorithm=options.algorithm
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;
 | |
| 
 | |
|       case ML.RL:
 | |
|         // Create learner instance
 | |
|         model = {}
 | |
|         options.environment=checkOptions(options.environment,{});
 | |
|         options.environment.getMaxNumActions=
 | |
|           checkOption(options.environment.getMaxNumActions,
 | |
|                       function () { return options.actions.length })
 | |
|         options.environment.getNumStates=
 | |
|           checkOption(options.environment.getNumStates,
 | |
|                       function () { return options.states.length })
 | |
|         var allowedActions=checkOption(options.environment.allowedActions, function () { return options.actions });
 | |
|         options.environment.allowedActions=
 | |
|           // Ensure that allowedActions return number array!
 | |
|           function (state) { 
 | |
|             return allowedActions(state).map(function (a) {
 | |
|               return options.actions.indexOf(a)
 | |
|             })
 | |
|           }  
 | |
|         var nextState = options.environment.nextState;
 | |
|         if (nextState) {
 | |
|           options.environment.nextState = function (state,action) {
 | |
|             return nextState(state,options.actions[action])
 | |
|           }
 | |
|         }
 | |
|         switch (options.kind) {
 | |
|           case ML.DQNAgent:                          
 | |
|             model = RL.DQNAgent(
 | |
|               options.environment,  
 | |
|               {
 | |
|                 alpha:options.alpha,gamma:options.gamma,epsilon:options.epsilon,
 | |
|                 experience_add_every:options.experience_add_every,
 | |
|                 experience_size:options.experience_size,
 | |
|                 learning_steps_per_iteration:options.learning_steps_per_iteration,
 | |
|                 tderror_clamp:options.tderror_clamp,
 | |
|                 num_hidden_units:options.num_hidden_units,
 | |
|                 update:options.update,
 | |
|                }
 | |
|             )
 | |
|             break;
 | |
|           case ML.DPAgent:
 | |
|             model = RL.DPAgent(
 | |
|               options.environment,  
 | |
|               {alpha:options.alpha,beta:options.beta,gamma:options.gamma,
 | |
|                epsilon:options.epsilon,lambda:options.lambda}
 | |
|             )
 | |
|             break;
 | |
|           case ML.TDAgent:
 | |
|             model = RL.TDAgent(
 | |
|               options.environment,  
 | |
|               // specs
 | |
|               {alpha:options.alpha,beta:options.beta,gamma:options.gamma,
 | |
|                epsilon:options.epsilon,lambda:options.lambda,
 | |
|                replacing_traces:options.replacing_traces,
 | |
|                smooth_policy_update:options.smooth_policy_update,
 | |
|                update:options.update,
 | |
|                planN:options.planN}
 | |
|             )
 | |
|             break;
 | |
|         }
 | |
|         model.algorithm = options.algorithm;
 | |
|         model.kind      = options.kind;
 | |
|         if (options.actions)  model.actions   = options.actions;
 | |
|         if (options.states)   model.states    = options.states;
 | |
|         if (options.rewards)  model.rewards   = options.rewards;
 | |
|         return model;
 | |
|         break;
 | |
| 
 | |
| 
 | |
| 
 | |
|       case ML.SLP:
 | |
|       case ML.MLP:
 | |
|         // typeof options = {x: number [][], 
 | |
|         //                   y: number number [][] | string [],
 | |
|         //                   hidden_layers?:[],epochs?:number,
 | |
|         //                   labels?:string [], features?: string [], 
 | |
|         //                   regression?,
 | |
|         //                   normalize?, bipolar?, eps?:number | number [], verbose?}
 | |
|         //
 | |
|         // y and MLP(learn) requires [[p1,p2,..],[p1,p2,..],..] with 0>=p>=1
 | |
|         //                                                           p:label probability
 | |
|         x=options.x;
 | |
|         if (Comp.obj.isArray(options.x) && typeof options.x[0] == 'number') 
 | |
|           x=wrap(options.x);   
 | |
|         if (Comp.obj.isMatrix(options.y)) 
 | |
|           y=options.y;
 | |
|         else if (Comp.obj.isArray(options.y) && typeof options.y[0] == 'number') 
 | |
|           y=wrap(options.y);        
 | |
|         else if (Comp.obj.isArray(options.y) && options.labels) {
 | |
|           y=options.y.map(function (l1) {
 | |
|             return options.labels.map(function (l2) {
 | |
|               return l1==l2?1:0;
 | |
|             });
 | |
|           });
 | |
|         } else throw 'ML.learn.MLP: invalid options';
 | |
|         if (options.normalize) {
 | |
|           // normalize each variable independently!?
 | |
|           var max=x[0].map(function (col) { return col}),
 | |
|               min=x[0].map(function (col) { return col});
 | |
|           x.forEach(function (row) { row.forEach(function (col,i) { 
 | |
|             max[i]=Math.max(max[i],col);
 | |
|             min[i]=Math.min(min[i],col) }) });
 | |
|           xshift=options.bipolar?-1:0;
 | |
|           xscale=max.map(function (x,i) { return (xshift?2:1)/((x-min[i])==0?1:x-min[i])});
 | |
|           xoffset=min;
 | |
|           x=x.map(function (row) { return row.map(function (col,i) { return xshift+(col-xoffset[i])*xscale[i] }) });
 | |
|           if (options.regression) {
 | |
|             // scale y, too, [0,1]
 | |
|             max=y[0].map(function (col) { return col});
 | |
|             min=y[0].map(function (col) { return col});
 | |
|             y.forEach(function (row) { row.forEach(function (col,i) { 
 | |
|               max[i]=Math.max(max[i],col);
 | |
|               min[i]=Math.min(min[i],col) }) });
 | |
|           
 | |
|             yshift=options.bipolar?-1:0;
 | |
|             yscale=max.map(function (x,i) { return (yshift?2:1)/((x-min[i])==0?1:x-min[i])});
 | |
|             yoffset=min;
 | |
|             y=y.map(function (row) { return row.map(function (col,i) { return yshift+(col-yoffset[i])*yscale[i] }) });
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         model = MLP({
 | |
|           input   : x,
 | |
|           output  : y,
 | |
|           n_ins   : x[0].length,
 | |
|           n_outs  : y[0].length,
 | |
|           hidden_layer_sizes:options.algorithm==ML.SLP?[]:(options.hidden_layers||[])
 | |
|         });
 | |
|         model.algorithm=options.algorithm;
 | |
|         model.labels=options.labels;
 | |
|         model.features=options.features;
 | |
|         model.xscale=options.normalize?{k:xscale,off:xoffset,shift:xshift}:undefined;
 | |
|         model.yscale=options.normalize&&options.regression?{k:yscale,off:yoffset,shift:yshift}:undefined;
 | |
|         model.nOutputs=y[0].length;
 | |
|         
 | |
|         MLP.code.set(model,'log level',options.verbose||0); // 0 : nothing, 1 : info, 2 : warning.
 | |
|         MLP.code.train(model,{
 | |
|           epochs : options.epochs||20000
 | |
|         });
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;
 | |
| 
 | |
|       case ML.SVM:
 | |
|         // typeof options = {x: number [][], 
 | |
|         //                   y: ({-1,1}|string) [],
 | |
|         //                   labels?:string|number [],
 | |
|         //                   threshold?:number|false,
 | |
|         //                   C?:numer,tol?:number,max_passes?:number,alpha_tol?:number,kernel?:{}}
 | |
|         
 | |
|         // If classes then multi-SVM (one for each class to be separated)!
 | |
|         if (!options.labels) {
 | |
|           model = SVM({
 | |
|             x:options.x,
 | |
|             y:options.y,
 | |
|             threshold:options.threshold,
 | |
|           });
 | |
|           model.algorithm=options.algorithm
 | |
|           SVM.code.train(model,{
 | |
|             C:options.C||1.0,
 | |
|             tol:options.tol||1e-4,
 | |
|             max_passes:options.max_passes||20,
 | |
|             alpha_tol:options.alpha_tol||1e-5,
 | |
|             kernel:options.kernel
 | |
|           });
 | |
|         } else {
 | |
|           model={};
 | |
|           model.algorithm=options.algorithm;
 | |
|           model._labels=options.labels;
 | |
|           model.svms=options.labels.map(function (cl) {
 | |
|             return SVM({
 | |
|               x:options.x,
 | |
|               y:options.y.map(function (y) { return y==cl?1:-1 }),
 | |
|               threshold:options.threshold,
 | |
|             });
 | |
|           });
 | |
|           
 | |
|           model.svms.forEach(function (svm) {
 | |
|             SVM.code.train(svm,{
 | |
|               C:options.C||1.0,
 | |
|               tol:options.tol||1e-4,
 | |
|               max_passes:options.max_passes||20,
 | |
|               alpha_tol:options.alpha_tol||1e-5,
 | |
|               kernel:options.kernel
 | |
|             });
 | |
|           });
 | |
|           // Create one SVM for each class
 | |
|           // Transform y vector          
 | |
|         }
 | |
|         model.time=Io.time()-t0;
 | |
|         return model;
 | |
|         break;
 | |
| 
 | |
|       case ML.TXT:
 | |
|         // typeof options = {data: string []}
 | |
|         model = TXT.create(options.data,{
 | |
|         });
 | |
|         model.algorithm=options.algorithm
 | |
|         return model;
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   preprocess:preprocess,
 | |
| 
 | |
|   print: function (model,indent,compact) {
 | |
|     switch (model.algorithm) {
 | |
|       case ML.DTI:
 | |
|         return DTI.print(model,indent,compact);
 | |
|       case ML.DT:
 | |
|       case ML.ICE:
 | |
|         return ICE.print(model,indent);
 | |
|       case ML.C45:
 | |
|         return C45.print(model,indent);
 | |
|       case ML.ID3:
 | |
|         return ID3.print(model,indent);
 | |
|     }
 | |
|   },
 | |
|   
 | |
|   // Only text module
 | |
|   similarity : TXT.similarity,
 | |
|   
 | |
|   stats : STAT,
 | |
|   
 | |
|   // Check model consistency
 | |
|   test: function (model,samples) {
 | |
|     var x,y,data,res,p=0.0;
 | |
|     switch (model.algorithm) {
 | |
|     
 | |
|       case ML.ANN:
 | |
|         data=preprocess(samples,'xmya',{features:model.features,target:model.target});
 | |
|         // TODO
 | |
|         break;
 | |
|         
 | |
|       case ML.C45:
 | |
|         // Sample row format: [x1,x2,..,y]
 | |
|         if (Comp.obj.isMatrix(samples)) {
 | |
|           samples.forEach(function (sample) {
 | |
|             x=sample.slice(0,sample.length-1);
 | |
|             y=sample[sample.length-1];
 | |
|             res= C45.classify(model,x);
 | |
|             if (res==y) p += 1;
 | |
|           });
 | |
|           return p/samples.length;
 | |
|         } else if (Comp.obj.isArray(samples)) {
 | |
|           x=samples.slice(0,samples.length-1);
 | |
|           y=samples[samples.length-1];
 | |
|           res = C45.classify(model,x);
 | |
|           return res==y?1.0:0.0
 | |
|         } else if (Comp.obj.isObj(samples) && model.features) {
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       case ML.TXT:
 | |
|         var model = model.string?{ data : [model.string] }:model;
 | |
|         if (Comp.obj.isArray(samples))
 | |
|           return samples.map(function (sample) { 
 | |
|             return TXT.classify(model,sample).match
 | |
|           });
 | |
|         else
 | |
|           return TXT.classify(model,samples).match;
 | |
|         break;
 | |
| 
 | |
|         
 | |
|     }
 | |
|   },
 | |
|   
 | |
| 
 | |
|   /** Update a learned model
 | |
|    *
 | |
|    */
 | |
|   update: function (model,options) {
 | |
|     switch (model.algorithm||options.algorithm) {
 | |
|     
 | |
|       case ML.RL:
 | |
|         switch (model.kind) {
 | |
|           case ML.DQNAgent:
 | |
|             return RL.DQNAgent.code.learn(model,options);
 | |
|             break;
 | |
|           case ML.DPAgent:  
 | |
|             return RL.DPAgent.code.learn(model,options);
 | |
|             break;
 | |
|           case ML.TDAgent:
 | |
|             return RL.TDAgent.code.learn(model,options);
 | |
|             break;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       case ML.DTI:
 | |
|         // typeof @options = {data: number [][], target:string, features: string [], eps?:number, maxdepth?:number} |
 | |
|         //                   {x: number [][], y:[], eps?:number, maxdepth?:number}
 | |
|         if (options.eps==_) options.eps=0;
 | |
|         if (options.maxdepth==_) options.maxdepth=20;
 | |
|         if (options.data && options.target && options.features)
 | |
|           model = DTI.update(model,options);
 | |
|         else if (options.x && options.y) {
 | |
|           if (options.x.length != options.y.length) throw 'ML.update.DTI: X and Y vector have different length';
 | |
|           data=options.x.slice();
 | |
|           data=data.map(function (row,i) {row.push(options.y[i]); return row});
 | |
|           features=Comp.array.init(data[0].length-1,function (i) { return String(i)});
 | |
|           target=String(data[0].length-1);
 | |
|           console.log(data,features,target)
 | |
|           model = DTI.update(model,{
 | |
|             data:data,
 | |
|             features:features,
 | |
|             target:target,
 | |
|             eps:options.eps,
 | |
|             maxdepth:options.maxdepth
 | |
|           });
 | |
|         } else throw 'ML.update.DTI: Invalid options';
 | |
|           
 | |
|         model.algorithm=options.algorithm;
 | |
|         return model;
 | |
| 
 | |
|       case ML.CNN:
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
|   ML:ML,
 | |
| };
 | |
|   
 | |
| ICE.ml=ml;
 | |
| CNN.ml=ml;
 | |
| ml.predict=ml.classify;
 | |
| ml.train=ml.learn;
 | |
| ml.best=ml.stats.utils.best;
 | |
| 
 | |
| module.exports = {
 | |
|   agent:ml,
 | |
|   classify:ml.classify,
 | |
|   column:ml.column,
 | |
|   compact:ml.compact,
 | |
|   depth:ml.depth,
 | |
|   entropy:STAT.entropy,
 | |
|   entropyN:STAT.entropyN,
 | |
|   entropyDep:STAT.entropyDep,
 | |
|   evaluate:ml.evaluate,
 | |
|   info:ml.info,
 | |
|   learn:ml.learn,
 | |
|   options:options,
 | |
|   preprocess:preprocess,
 | |
|   print:ml.print,
 | |
|   stats:STAT,
 | |
|   test:ml.test,
 | |
|   unique:ml.unique,
 | |
|   update:ml.update,
 | |
|   ML:ML,
 | |
|   current:function (module) { current=module.current; Aios=module; }
 | |
| }
 |