NeuralMesh
  • Class
  • Tree

Classes

  • AbstractNetwork
  • Controller
  • layer
  • ManagedNetwork
  • Model
  • mysql
  • Navigation
  • network
  • NeuralMesh
  • neuron
  • nmesh
  • synapse
  • train
  • UnmanagedNetwork
  • users
  • validation
  1 <?php
  2 /**
  3  * NMesh object. This class is the base class.
  4  * 
  5  * Warning: A lot of effort and sanity has gone into optimizing
  6  * this, so some methods may not look pretty but it's all for the greater
  7  * good of performance. PHP is not typically recognised for it's speed,
  8  * nor are neural networks, so this combination was never destined to be
  9  * enjoyable or even possible yet here it is in all its glory.
 10  * 
 11  * Based on the PHPNN class
 12  * 
 13  * @author Louis Stowasser
 14  */
 15 class nmesh
 16 {
 17     /** Array of layer objects */
 18     public $layer = array();
 19     /** Count of inputs */
 20     public $inputs;
 21     /** Count of outputs */
 22     public $outputs;
 23     /** Momentum rate generalized for entire network */
 24     static public $momentumrate = 0.5;
 25     /** Cached output */
 26     static public $cache;
 27     
 28     function sigmoid($value) {
 29         return 1 / (1+exp(-1*$value));
 30     }
 31     
 32     /**
 33      * Takes some input and runs the network
 34      * @param $inputarray Input data
 35      * @return The output data
 36      */ 
 37     function run($inputarray) 
 38     {
 39         $output = array();
 40         $l_count = count($this->layer);
 41         
 42         for($l = 0; $l < $l_count; ++$l) { // Now we walk through each layer of the net
 43             
 44             foreach($this->layer[$l]->neuron as $neuron) {
 45                 $inputs = ($l===0) ? $inputarray : $output[$l];
 46                 
 47                 $x = 0; $sum = 0;
 48                 foreach($neuron->synapse as $synapse) {
 49                     $synapse->input = $inputs[$x];
 50                     $sum += $synapse->weight * $synapse->input;
 51                     ++$x;
 52                 }
 53 
 54                 $value = 1/(1+exp(-1*($sum+$neuron->bias)));
 55                 $neuron->value = $value;
 56                 $output[$l+1][] = $value;
 57             }
 58         }
 59         $data = $output[$l];
 60         self::$cache = $data;
 61         return $data;
 62     }
 63     
 64     /**
 65      * Do a quick unsupervised training set
 66      * @param $id ID of the network
 67      * @param $epochs Amount of epochs to run
 68      * @param $inputs String of inputs
 69      * @param $outputs String of desired outputs
 70      * @return Array of results
 71      */
 72     function quick_train($id,$epochs,$lr,$inputs,$outputs) {
 73         $inputarray = str_split($inputs); //convert from string to array
 74         $outputarray = str_split($outputs);
 75         
 76         $start = microtime(true);
 77         for($i=0;$i<$epochs;$i++) {
 78             $end_mse = $this->train($inputarray,$outputarray,$lr);
 79             if($i===1) $start_mse = $end_mse; 
 80         }
 81         $exectime = microtime(true)-$start;
 82         return array("startmse"=>$start_mse,"endmse"=>$end_mse,"time"=>$exectime);
 83     }
 84     
 85     /**
 86      * Main function to train the network based on some inputs and
 87      * desired outputs
 88      * @param $inputarray Inputs to train
 89      * @param $outputarray Desired outputs
 90      * @param $learningrate The rate at which it learns
 91      * @return The global MSE (how intelligent the network is)
 92      */
 93     function train($inputarray,$outputarray,$learningrate)
 94     {
 95         $this->run($inputarray);  //Run a feedforward pass as normal
 96         return $this->calculate_deltas($outputarray,$learningrate);
 97     }
 98     
 99     /**
100      * This peforms the backpropagation algorithm on the network
101      * @param $outputarray Based on the last run, teach it to return this
102      * @param $lr The learning rate
103      * @return The global MSE of this training epoch
104      */
105     function calculate_deltas($outputarray,$lr)
106     {
107         $mse_sum = 0;
108         $l_count = count($this->layer)-1;
109         $m = nmesh::$momentumrate;
110         $error = array();
111         $output_count = count($this->layer[$l_count]->neuron);
112         
113         for($l = $l_count; $l >= 0; --$l) {
114             
115             $error[$l] = array_fill(0,count($this->layer[$l]->neuron[0]->synapse),0);
116             
117             foreach($this->layer[$l]->neuron as $n=>$neuron) 
118             {
119                 if($l===$l_count) {
120                     $n_error = $outputarray[$n] - $neuron->value;
121                     $mse_sum += $n_error * $n_error;
122                 } else $n_error = $error[$l+1][$n];
123                 
124                 $delta = $n_error * $neuron->value * (1 - $neuron->value);
125                 
126                 foreach($neuron->synapse as $s=>$synapse)
127                 {
128                     $wc = $delta * $synapse->input * $lr + $synapse->momentum * $m;
129                     $synapse->momentum = $wc;
130                     $synapse->weight += $wc;
131                     $error[$l][$s] += $delta * $synapse->weight;
132                 }
133                 //And lets go ahead and adjust the bias too
134                 $biaschange = $delta * $lr + $neuron->momentum * $m;
135                 $neuron->momentum = $biaschange;
136                 $neuron->bias += $biaschange;
137             }
138         }
139         return $mse_sum / $output_count;
140     }
141     
142     /*
143      * Basic sigmoid derivative
144      */
145     function sigmoid_derivative($value)
146     {
147         return $value * (1 - $value);
148     }
149     
150     /**
151      * Network manipulation to remove an amount of neurons
152      * @param $inputs Amount of input neurons to remove
153      */
154     function remove_inputs($inputs) {
155         if($this->inputs - $inputs < 1) 
156             throw new Exception("Cannot remove neurons!"); //can't remove
157         
158         $this->inputs -= $inputs;
159         for($i=0;$i<$inputs;$i++) {
160             $n_count = count($this->layer[0]->neuron);
161             for($n=0;$n<$n_count;$n++) {
162                 $this->layer[0]->neuron[$n]->remove_synapse();
163             }
164         }
165     }
166     
167     /**
168      * Add an amount of input neurons
169      * @param $inputs Amount of input neurons to add
170      */
171     function add_inputs($inputs) {
172         $this->inputs += $inputs;
173         for($i=0;$i<$inputs;$i++) {
174             $n_count = count($this->layer[0]->neuron);
175             for($n=0;$n<$n_count;$n++) {
176                 $this->layer[0]->neuron[$n]->add_synapse();
177             }
178         } 
179     }
180     
181     /**
182      * Remove some outputs
183      * @param $outputs Amount of output neurons to remove
184      */
185     function remove_outputs($outputs) {
186         if($this->outputs - $outputs < 1) throw new Exception("Cannot remove that many outputs!");
187         $this->outputs -= $outputs;
188         for($i=0;$i<$outputs;$i++) {
189             $this->layer[count($this->layer)-1]->remove_neuron();
190         }
191     }
192     
193     /**
194      * Add some outputs
195      * @param $outputs Amount to add
196      */
197     function add_outputs($outputs) {
198         $this->outputs += $outputs;
199         for($i=0;$i<$outputs;$i++) {
200             $this->layer[count($this->layer)-1]->add_neuron();
201         }
202     }
203     
204     /**
205      * Add a hidden layer to the network
206      * @param $neuronal_bias Bias
207      * @param $initial_weightrange Range of random weights
208      */
209     function add_layer($neuronal_bias=1,$initial_weightrange=1) {
210         $hidden_neurons = count($this->layer[0]->neuron);
211         array_splice($this->layer,
212                      count($this->layer)-1,//because count is base 0
213                      0,
214                      //wrapped in array() so it doesnt lose its object cast
215                      array(new layer($hidden_neurons,$hidden_neurons,$neuronal_bias,$initial_weightrange)));
216     }
217     
218     /**
219      * Add a neuron to hidden layers
220      * @param $count Amount of neurons to add
221      * @param $bias
222      * @param $weightrange
223      */
224     function add_neuron($count=1,$bias=1,$weightrange=1) {
225         for($i=0;$i<$count;$i++) { //add a count
226             for($l=0;$l<count($this->layer)-1;$l++) { //loop over hidden layers
227                 $this->layer[$l]->add_neuron($bias,$weightrange);   
228                 for($n=0;$n<count($this->layer[$l+1]->neuron);$n++) {
229                     //loop over the neurons and add an extra synapse
230                     $this->layer[$l+1]->neuron[$n]->add_synapse();
231                 }
232             }
233         }
234     }
235     
236     /**
237      * Remove a layer from the network
238      * @param $layer Index of the layer to remove (must be hidden layer)
239      */
240     function remove_layer($layer=null) {
241         //if is not the output layer and has more than one hidden layer
242         $layer = is_null($layer) ? count($this->layer)-2 : $layer;
243         if(count($this->layer) > 2 && $this->is_hidden_layer($layer)) {
244             array_splice($this->layer,$layer,1);
245         }
246     }
247     
248     /**
249      * Remove a neuron from hidden layers
250      * @param $neuron Index of neuron
251      */
252     function remove_neuron($count=0) {
253         if(count($this->layer[0]->neuron) - $count < 1) 
254             throw new Exception("Cannot remove neurons!");
255         
256         for($l=0;$l<count($this->layer)-1;$l++) { //loop through hidden layers
257 
258             for($c=0;$c<$count;$c++) $this->layer[$l]->remove_neuron();
259 
260             for($n=0;$n<count($this->layer[$l+1]->neuron);$n++) {
261                 //loop over the neurons and remove a synapse
262                 for($c=0;$c<$count;$c++) $this->layer[$l+1]->neuron[$n]->remove_synapse($count);
263             }
264         }
265     }
266     
267     /**
268      * Constructor for creating a neural network object
269      */
270     function nmesh($input_neurons,$output_neurons,$hidden_neurons_per_layer,$hidden_layers,$neuronal_bias=1,$initial_weightrange=1)
271     {
272         $this->inputs = $input_neurons;
273         $this->outputs= $output_neurons;
274         $firstlayerflag = 0;
275         $total_layers = $hidden_layers+1;
276         
277         $this->layer[0] = new layer($hidden_neurons_per_layer,$input_neurons,$neuronal_bias,$initial_weightrange);
278         for($i=1;$i<$total_layers-1;$i++)
279         {
280             $inputs = $input_neurons;
281             if($firstlayerflag==1) //second hidden layer
282             {
283                 $inputs = $hidden_neurons_per_layer; //use the hidden neurons
284             }
285             $this->layer[$i] = new layer($hidden_neurons_per_layer,$inputs,$neuronal_bias,$initial_weightrange);
286             $firstlayerflag=1;
287         }
288         $this->layer[$total_layers-1] = new layer($output_neurons,$hidden_neurons_per_layer,$neuronal_bias,$initial_weightrange);
289     }
290 }
291 
292 /**
293  * Layer object holds an array of neurons. Mainly an organisation class.
294  * @author Louis Stowasser
295  */
296 class layer
297 {
298     /** Array of neuron objects */
299     public $neuron = array();
300     
301     /**
302      * Adds a neuron to its array
303      */
304     function add_neuron($bias=1,$weightrange=1) {
305         $this->neuron[count($this->neuron)] = new neuron(count($this->neuron[0]->synapse),$bias,$weightrange);
306     }
307     
308     /**
309      * Remove a neuron from the array
310      */
311     function remove_neuron() {
312         array_splice($this->neuron,0,1);        
313     }
314     
315     /**
316      * Constructor for the layer object
317      * @param $neurons Amount of neurons
318      * @param $inputs Amount of inputs
319      */
320     function layer($neurons,$inputs,$bias,$weightrange)
321     {
322         for($i=0;$i<$neurons;$i++)
323         {
324             $this->neuron[$i] = new neuron($inputs,$bias,$weightrange);
325         }
326     }
327 }
328 
329 /**
330  * The Neuron class does most of the work
331  * @author Louis Stowasser
332  */
333 class neuron
334 {
335     /** neurons value */
336     public $value;
337     /** bias value */
338     public $bias;
339     /** temporary error */
340     //public $error;
341     /** momentum value */
342     public $momentum;
343     /** array of synapse objects */
344     public $synapse = array();
345     
346     /**
347      * Cleanup unnecessary variables to save memory and serialization time
348      */
349     function cleanup()
350     {
351         unset($this->error);
352         unset($this->value);
353     }
354     
355     /**
356      * Adjusts the weights of the synapses as well as the bias and momentum
357      * and cleans up uneeded varibles when finished.
358      * @param $learningrate The learning rate
359      */
360     function adjust_weights_clean($learningrate,$delta)
361     {
362         $m = nmesh::$momentumrate;
363         
364         foreach($this->synapse as $synapse)
365         {
366             $weightchange = $delta * $synapse->input * $learningrate + $synapse->momentum * $m;
367             $synapse->momentum = $weightchange;
368             $synapse->weight += $weightchange;
369             
370             unset($synapse->input); unset($synapse->delta);
371         }
372         //And lets go ahead and adjust the bias too
373         $biaschange = $delta * $learningrate + $this->momentum * $m;
374         $this->momentum = $biaschange;
375         $this->bias += $biaschange;
376     }
377     
378     /**
379      * Adjusts the weights of the synapses as well as the bias and momentum
380      * but keeps the variables and sets the synapses delta
381      * @param $learningrate The learning rate
382      */
383     function adjust_weights($learningrate,$delta)
384     {
385         $m = nmesh::$momentumrate;
386         
387         foreach($this->synapse as $synapse)
388         {
389             $synapse->delta = $delta;
390             $weightchange = $delta * $synapse->input * $learningrate + $synapse->momentum * $m;
391             $synapse->momentum = $weightchange;
392             $synapse->weight += $weightchange;
393         }
394         //And lets go ahead and adjust the bias too
395         $biaschange = $delta * $learningrate + $this->momentum * $m;
396         $this->momentum = $biaschange;
397         $this->bias += $biaschange;
398     }
399     
400     /**
401      * Set the synapse weights to a random number within a range
402      * @param $weightrange Range of the random weight
403      */
404     function randomize_weights($weightrange)
405     {       
406         $s_count = count($this->synapse);
407         for($i=0;$i<$s_count;$i++)
408         {
409             $this->synapse[$i]->randomize_weight($weightrange);
410         }
411     }
412     
413     /**
414      * Mathematical sigmoid function, mainly because PHP doesn't have it natively.
415      * @param $value Number to return the sigmoid of
416      * @return Sigmoid of given number
417      */
418     function sigmoid($value)
419     {
420         return 1 / (1+exp(-1*$value));
421     }
422     
423     /**
424      * Add a synapse to the synapse array
425      * @param $weightrange Range for random weights
426      */
427     function add_synapse($weightrange=1) {
428         $this->synapse[count($this->synapse)] = new synapse($weightrange);
429     }
430     
431     /**
432      * Remove a synapse from the synapse array
433      */
434     function remove_synapse() {
435         array_splice($this->synapse,0,1);
436     }
437     
438     /**
439      * Neuron constructor
440      * @param $inputs Amount of inputs
441      */
442     function neuron($inputs,$bias,$weightrange)
443     {
444         unset($this->value); //only set when needed
445         unset($this->error);
446         $this->bias = $bias;
447         $this->momentum = 0;
448         
449         for($i=0;$i<$inputs;$i++)
450         {
451             $this->synapse[$i] = new synapse($weightrange);
452         }
453     }
454 }
455 
456 /**
457  * Synapse class
458  * @author Louis Stowasser
459  */
460 class synapse
461 {
462     /** Input value */
463     public $input;
464     /** Weight of the synapse */
465     public $weight;
466     /** Value of error */
467     //public $delta;
468     /** Momentum value */
469     public $momentum;
470     
471     /**
472      * Cleanup unnecessary variables to save memory and serialization time
473      */
474     function cleanup()
475     {
476         unset($this->input);
477         unset($this->delta);
478     }
479     
480     /**
481      * Generate a random weight for the synapse
482      * @param $weightrange Range to generate in
483      */
484     function randomize_weight($weightrange)
485     {
486         $this->weight = (mt_rand(0,$weightrange*2000)/1000)-$weightrange;
487     }
488     
489     /**
490      * Synapse constructor
491      * @param $weightrange Range of random weights
492      */
493     function synapse($weightrange)
494     {
495         unset($this->input);
496         unset($this->delta);
497         $this->momentum = 0;
498         $this->randomize_weight($weightrange);
499     }
500 }
501 ?>
NeuralMesh API documentation generated by ApiGen