Management of genome's training with callback + fitness user-defined + generation&genomes management to do in NeuralNetworkCore::learn()
This commit is contained in:
parent
db41a36af4
commit
e16048b569
|
@ -15,9 +15,17 @@
|
||||||
/************************************************
|
/************************************************
|
||||||
**** LOCAL ATTRIBUTES ****
|
**** LOCAL ATTRIBUTES ****
|
||||||
************************************************/
|
************************************************/
|
||||||
public $layers; // Number of layers
|
private $layers; // Number of hidden layers
|
||||||
public $neurons; // Number of neurons per layer
|
|
||||||
public $synapses; // Synapses between neurons
|
private $inputN; // Number of input neurons
|
||||||
|
private $neurons; // Number of neurons for each hidden layer
|
||||||
|
private $outputN; // Number of output neurons
|
||||||
|
|
||||||
|
private $synapses; // Synapses between neurons
|
||||||
|
|
||||||
|
private $callback; // callback training function
|
||||||
|
|
||||||
|
private $fitness; // Genome's fitness
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
|
@ -27,8 +35,10 @@
|
||||||
/* CONSTRUCTOR
|
/* CONSTRUCTOR
|
||||||
*
|
*
|
||||||
* -- RANDOM CREATION --
|
* -- RANDOM CREATION --
|
||||||
* @layers<int> Number of layers to manage
|
* @layers<int> Number of hidden layers to manage
|
||||||
* @neurons<int> Number of neurons per layer
|
* @neurons<int> Number of neurons per hidden layer
|
||||||
|
* @inN<int> Number of input neurons
|
||||||
|
* @outN<int> Number of output neurons
|
||||||
*
|
*
|
||||||
* -- CLONING --
|
* -- CLONING --
|
||||||
* @base<Genom> Genome to clone to
|
* @base<Genom> Genome to clone to
|
||||||
|
@ -50,8 +60,8 @@
|
||||||
$this->construct_crossover($argv[0], $argv[1]);
|
$this->construct_crossover($argv[0], $argv[1]);
|
||||||
|
|
||||||
/* (3) If RandomCreation */
|
/* (3) If RandomCreation */
|
||||||
else if( $argc > 1 && abs(intval($argv[0])) === $argv[0] && abs(intval($argv[1])) === $argv[1] )
|
else if( $argc > 3 && is_numeric($argv[0]) && is_numeric($argv[1]) && is_numeric($argv[2]) && is_numeric($argv[3]) )
|
||||||
$this->construct_new($argv[0], $argv[1]);
|
$this->construct_new($argv[0], $argv[1], $argv[2], $argv[3]);
|
||||||
|
|
||||||
/* (4) If InheritanceCreation (clone) */
|
/* (4) If InheritanceCreation (clone) */
|
||||||
else if( $argc > 0 && $argv[0] instanceof Genome )
|
else if( $argc > 0 && $argv[0] instanceof Genome )
|
||||||
|
@ -60,19 +70,23 @@
|
||||||
/* (5) If no match */
|
/* (5) If no match */
|
||||||
else
|
else
|
||||||
throw new \Error('Invalid Genome constructor\'s arguments.');
|
throw new \Error('Invalid Genome constructor\'s arguments.');
|
||||||
|
|
||||||
|
$this->callback = function(){};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BUILDS A Genome RANDOMLY WITH PARAMETERS
|
/* BUILDS A Genome RANDOMLY WITH PARAMETERS
|
||||||
*
|
*
|
||||||
* @layers<int> The number of hidden layers to manage
|
* @layers<int> The number of hidden layers to manage
|
||||||
* @neurons<int> The number neurons per layer
|
* @neurons<int> The number neurons per layer
|
||||||
|
* @inN<int> Number of input neurons
|
||||||
|
* @outN<int> Number of output neurons
|
||||||
*
|
*
|
||||||
* @return created<Boolean> If Genome has been successfully created
|
* @return created<Boolean> If Genome has been successfully created
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private function construct_new($layers=-1, $neurons=-1){
|
private function construct_new($layers, $neurons, $inN, $outN){
|
||||||
/* (1) Checks parameters */
|
/* (1) Checks parameters */
|
||||||
if( abs(intval($layers)) !== $layers || abs(intval($neurons)) !== $neurons )
|
if( abs(intval($layers)) !== $layers || abs(intval($neurons)) !== $neurons || abs(intval($inN)) !== $inN || abs(intval($outN)) !== $outN )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// set layers
|
// set layers
|
||||||
|
@ -80,10 +94,12 @@
|
||||||
|
|
||||||
/* (2) Store number of neurons */
|
/* (2) Store number of neurons */
|
||||||
$this->neurons = $neurons;
|
$this->neurons = $neurons;
|
||||||
|
$this->inputN = $inN;
|
||||||
|
$this->outputN = $outN;
|
||||||
|
|
||||||
/* (3) Creating random synapses */
|
/* (3) Creating random synapses */
|
||||||
$this->synapses = [];
|
$this->synapses = [];
|
||||||
for( $i = 0, $l = $layers*pow($neurons,2) ; $i < $l ; $i++ )
|
for( $i = 0, $l = $neurons*($inN+$outN+$neurons*$layers) ; $i < $l ; $i++ )
|
||||||
$this->synapses[$i] = rand(self::MIN, self::MAX) / self::MAX;
|
$this->synapses[$i] = rand(self::MIN, self::MAX) / self::MAX;
|
||||||
|
|
||||||
// Success status
|
// Success status
|
||||||
|
@ -124,19 +140,26 @@
|
||||||
if( !($father instanceof Genome) || !($mother instanceof Genome) )
|
if( !($father instanceof Genome) || !($mother instanceof Genome) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* (2) Checks number of layers+neurons (same species) */
|
/* (2) Checks the attributes (same species) */
|
||||||
if( $father->layers !== $mother->layers || $father->neurons !== $mother->neurons )
|
$diffAttr = $father->layers !== $mother->layers;
|
||||||
|
$diffAttr = $diffAttr || $father->inputN !== $mother->inputN;
|
||||||
|
$diffAttr = $diffAttr || $father->neurons !== $mother->neurons;
|
||||||
|
$diffAttr = $diffAttr || $father->outputN !== $mother->outputN;
|
||||||
|
|
||||||
|
if( $diffAttr )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* (3) Set layer count */
|
/* (3) Set layer count */
|
||||||
$this->layers = $father->layers;
|
$this->layers = $father->layers;
|
||||||
|
|
||||||
/* (4) Set neurons number */
|
/* (4) Set neurons number */
|
||||||
|
$this->inputN = $father->inputN;
|
||||||
$this->neurons = $father->neurons;
|
$this->neurons = $father->neurons;
|
||||||
|
$this->outputN = $father->inputN;
|
||||||
|
|
||||||
/* (5) Do random crossover for synapses */
|
/* (5) Do random crossover for synapses */
|
||||||
$this->synapses = [];
|
$this->synapses = [];
|
||||||
for( $i = 0, $l = $this->layers*pow($this->neurons, 2) ; $i < $l ; $i++ )
|
for( $i = 0, $l = $this->neurons*($this->inputN+$this->outputN+$this->neurons*$this->layers) ; $i < $l ; $i++ )
|
||||||
if( !!rand(0,1) ) $this->synapses[$i] = $father->synapses[$i];
|
if( !!rand(0,1) ) $this->synapses[$i] = $father->synapses[$i];
|
||||||
else $this->synapses[$i] = $mother->synapses[$i];
|
else $this->synapses[$i] = $mother->synapses[$i];
|
||||||
|
|
||||||
|
@ -145,6 +168,39 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
**** Setters ****
|
||||||
|
************************************************/
|
||||||
|
|
||||||
|
/* SETS THE CALLBACK FUNCTION
|
||||||
|
*
|
||||||
|
* @callback<Function> Callback function to train with
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function setCallback($callback=null){
|
||||||
|
/* (1) Checks @callback argument */
|
||||||
|
if( !is_callable($callback) )
|
||||||
|
throw new \Error('Wrong argument for Genome\'s callback function.');
|
||||||
|
|
||||||
|
/* (2) Set callback function */
|
||||||
|
$this->callback = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SETS THE FITNESS OF THE GENOME
|
||||||
|
*
|
||||||
|
* @fitness<double> Calculated fitness of the genome
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function setFitness($fitness=null){
|
||||||
|
/* (1) Checks @fitness argument */
|
||||||
|
if( !is_numeric($fitness) )
|
||||||
|
throw new \Error('Wrong argument for specifying Genome\'s fitness.');
|
||||||
|
|
||||||
|
/* (2) Set fitness */
|
||||||
|
$this->fitness = floatval($fitness);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
**** Genome Actions ****
|
**** Genome Actions ****
|
||||||
************************************************/
|
************************************************/
|
||||||
|
@ -160,19 +216,9 @@
|
||||||
throw new \Error('Invalid threshold for Genome mutation.');
|
throw new \Error('Invalid threshold for Genome mutation.');
|
||||||
|
|
||||||
/* (2) Calculates how many neurons/synapses to mutate */
|
/* (2) Calculates how many neurons/synapses to mutate */
|
||||||
$neuronsMutations = round( (count($this->neurons) - 1) * $threshold );
|
|
||||||
$synapsesMutations = round( (count($this->synapses) - 1) * $threshold );
|
$synapsesMutations = round( (count($this->synapses) - 1) * $threshold );
|
||||||
|
|
||||||
/* (3) Choose random neurons' indexes */
|
/* (3) Choose random synapses' indexes */
|
||||||
$iNeurons = [];
|
|
||||||
while( count($iNeurons) < $neuronsMutations ){
|
|
||||||
$r = rand(0, count($this->neurons)-1);
|
|
||||||
|
|
||||||
if( !in_array($r, $iNeurons) )
|
|
||||||
$iNeurons[] = $r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (4) Choose random synapses' indexes */
|
|
||||||
$iSynapses = [];
|
$iSynapses = [];
|
||||||
while( count($iSynapses) < $synapsesMutations ){
|
while( count($iSynapses) < $synapsesMutations ){
|
||||||
$r = rand(0, count($this->synapses)-1);
|
$r = rand(0, count($this->synapses)-1);
|
||||||
|
@ -181,11 +227,7 @@
|
||||||
$iSynapses[] = $r;
|
$iSynapses[] = $r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (5) Update chosen neurons */
|
/* (4) Update chosen synapses */
|
||||||
for( $i = 0, $l = count($iNeurons) ; $i < $l ; $i++ )
|
|
||||||
$this->neurons[$iNeurons[$i]] = rand(self::MIN, self::MAX) / self::MAX;
|
|
||||||
|
|
||||||
/* (6) Update chosen synapses */
|
|
||||||
for( $i = 0, $l = count($iSynapses) ; $i < $l ; $i++ )
|
for( $i = 0, $l = count($iSynapses) ; $i < $l ; $i++ )
|
||||||
$this->synapses[$iSynapses[$i]] = rand(self::MIN, self::MAX) / self::MAX;
|
$this->synapses[$iSynapses[$i]] = rand(self::MIN, self::MAX) / self::MAX;
|
||||||
}
|
}
|
||||||
|
@ -193,20 +235,25 @@
|
||||||
/* CALCULATES THE OUTPUT OF THE GENOME
|
/* CALCULATES THE OUTPUT OF THE GENOME
|
||||||
*
|
*
|
||||||
* @input<Array> Input for which we want fitness
|
* @input<Array> Input for which we want fitness
|
||||||
|
* @callback<Function> [OPTIONAL] Callback function
|
||||||
*
|
*
|
||||||
* @return output<double> Output calculated with this input
|
* @note: will call @callback function with INPUTS as 1st argument and OUTPUTS as 2nd arguments
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function process($input=null){
|
public function train($input=null, $callback=null){
|
||||||
/* (1) Checks @input argument */
|
/* (1) Checks @input argument */
|
||||||
if( !is_array($input) || count($input) !== $this->neurons )
|
if( !is_array($input) || count($input) !== $this->inputN )
|
||||||
throw new \Error('Invalid @input for Genome\'s output calculation.');
|
throw new \Error('Invalid @input for Genome\'s training.');
|
||||||
|
|
||||||
|
/* (2) Checks optional @callback argument */
|
||||||
|
if( is_callable($callback) )
|
||||||
|
$this->setCallback($callback);
|
||||||
|
|
||||||
|
|
||||||
/* [1] Set temporary calculation data
|
/* [1] Set temporary calculation data
|
||||||
=========================================================*/
|
=========================================================*/
|
||||||
/* (1) Set temporary neurons data */
|
/* (1) Set temporary neurons data */
|
||||||
$neurons = array_merge( $input, array_fill(0, $this->neurons*$this->layers, 0) );
|
$neurons = array_merge( $input, array_fill(0, $this->neurons*$this->layers, 0), array_fill(0, $this->outputN, 0) );
|
||||||
|
|
||||||
/* (2) Set temporary synapses data */
|
/* (2) Set temporary synapses data */
|
||||||
$synapses = $this->synapses;
|
$synapses = $this->synapses;
|
||||||
|
@ -216,39 +263,122 @@
|
||||||
=========================================================*/
|
=========================================================*/
|
||||||
/* (1) For each hidden layer
|
/* (1) For each hidden layer
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
for( $l = 1 ; $l < $this->layers+1 ; $l++ ){
|
foreach($this->layersIterator() as $lr=>$l){
|
||||||
|
|
||||||
/* (2) For each neuron of this layer
|
/* (2) For each neuron of this layer
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
for( $n = $l*$this->neurons, $nl = ($l+1)*$this->neurons ; $n < $nl ; $n++ ){
|
foreach($this->neuronsIterator($l) as $nr=>$n){
|
||||||
|
|
||||||
$neurons[$n] = 0;
|
$neurons[$n] = 0;
|
||||||
|
|
||||||
/* (3) For each synapse between current neuron and last layer
|
/* (3) For each synapse between current neuron and last layer
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
for( $s = ($l-1)*pow($this->neurons,2), $sl = $l*pow($this->neurons,2) ; $s < $sl ; $s++ )
|
foreach($this->synapsesIterator($l, $nr) as $sr=>$s){
|
||||||
$neurons[$n] += $synapses[$s] * $neurons[ floor($s/$this->neurons) ];
|
// echo "current: n#$nr/l#$l = s#$s * n#$sr\n";
|
||||||
// newNeuron += synapse*lastLayerNeuron
|
// echo "calc: ${neurons[$n]} += ${synapses[$s]} * ${neurons[$sr]}\n";
|
||||||
|
$neurons[$n] += $synapses[$s] * $neurons[$sr];
|
||||||
|
// echo "result: ${neurons[$n]}\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
/* Divide the sum to make a mean */
|
|
||||||
$neurons[$n] /= $this->neurons;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (4) Calculates single output
|
|
||||||
---------------------------------------------------------*/
|
|
||||||
$output = 0;
|
|
||||||
|
|
||||||
for( $n = ($this->layers-1)*$this->neurons, $nl = $this->layers*$this->neurons ; $n < $nl ; $n++ )
|
/* [3] Callback the output layer's values
|
||||||
$output += $neurons[$n];
|
|
||||||
|
|
||||||
|
|
||||||
/* [3] Returns output
|
|
||||||
=========================================================*/
|
=========================================================*/
|
||||||
return $output / $this->neurons;
|
call_user_func($this->callback, $input, array_slice($neurons, -$this->outputN) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
**** Utilities ****
|
||||||
|
************************************************/
|
||||||
|
|
||||||
|
/* RETURNS ITERATOR THROUGH EACH LAYER
|
||||||
|
*
|
||||||
|
* @return indexes<yield> Iterator through each layer (relative->absolute)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function layersIterator(){
|
||||||
|
for( $l = 1 ; $l < $this->layers+2 ; $l++ )
|
||||||
|
yield $l => $l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RETURNS AN ITERATOR ON THE NEURONS OF A LAYER
|
||||||
|
*
|
||||||
|
* @layer<int> Layer to browse
|
||||||
|
*
|
||||||
|
* @return indexes<yield> Iterator through the neurons indexes of the layer (relative->absolute)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function neuronsIterator($layer){
|
||||||
|
/* (1) Formats @layer argument */
|
||||||
|
if( $layer < 0 ) $layer = 0;
|
||||||
|
if( $layer > $this->layers+1 ) $layer = $this->layers+1;
|
||||||
|
|
||||||
|
$layer = intval($layer);
|
||||||
|
|
||||||
|
/* (2) If between input/hidden */
|
||||||
|
if( $layer === 0 ){
|
||||||
|
$offset = 0;
|
||||||
|
for( $n = $offset, $nl = $this->inputN ; $n < $nl ; $n++ )
|
||||||
|
yield $n-$offset => $n;
|
||||||
|
|
||||||
|
/* (3) If between hidden/output */
|
||||||
|
}else if( $layer == $this->layers+1 ){
|
||||||
|
$offset = $this->inputN + $this->neurons * $this->layers;
|
||||||
|
for( $n = $offset, $nl = $this->inputN + $this->neurons * $this->layers + $this->outputN ; $n < $nl ; $n++ )
|
||||||
|
yield $n-$offset => $n;
|
||||||
|
|
||||||
|
/* (2) If in between hidden layer */
|
||||||
|
}else{
|
||||||
|
$offset = $this->inputN + $this->neurons * ($layer-1);
|
||||||
|
for( $n = $offset, $nl = $this->inputN+$this->neurons*$layer ; $n < $nl ; $n++ )
|
||||||
|
yield $n-$offset => $n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RETURNS AN ITERATOR ON THE SYNAPSES BETWEEN A NEURON AND ITS PREVIOUS LAYER'S NEURONS
|
||||||
|
*
|
||||||
|
* @layer<int> Destination layer
|
||||||
|
* @neuron<int> Destination neuron
|
||||||
|
*
|
||||||
|
* @return indexes<yield> Iterator through the synapses indexes between the destination neuron and the source layer's neurons (neuronIndex->SynapseIndex)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function synapsesIterator($layer, $neuron){
|
||||||
|
/* (1) Formats @layer argument */
|
||||||
|
if( $layer < 1 ) $layer = 1;
|
||||||
|
if( $layer > $this->layers+1 ) $layer = $this->layers+1;
|
||||||
|
|
||||||
|
$layer = intval($layer);
|
||||||
|
$prev = $layer-1;
|
||||||
|
|
||||||
|
/* (2) If between input/hidden */
|
||||||
|
if( $layer === 1 ){
|
||||||
|
$offset = 0;
|
||||||
|
for( $s = $offset+$neuron, $sl = $this->inputN*$this->neurons ; $s < $sl ; $s += $this->neurons )
|
||||||
|
yield ($s-$offset-$neuron)/$this->neurons => $s;
|
||||||
|
|
||||||
|
|
||||||
|
/* (3) If between hidden/output */
|
||||||
|
}else if( $layer == $this->layers+1 ){
|
||||||
|
$offset = $this->neurons * ($this->inputN+$this->neurons*$this->layers);
|
||||||
|
for( $s = $offset+$neuron, $sl = $offset+$this->neurons*$this->outputN ; $s < $sl ; $s += $this->outputN )
|
||||||
|
yield $this->inputN+$this->neurons*($this->layers-1) + ($s-$offset-$neuron)/$this->outputN => $s;
|
||||||
|
|
||||||
|
/* (2) If in between hidden layer */
|
||||||
|
}else{
|
||||||
|
$offset = $this->neurons * ($this->inputN+$this->neurons*($prev-1));
|
||||||
|
for( $s = $offset+$neuron, $sl = $offset+$this->neurons*$this->neurons ; $s < $sl ; $s += $this->neurons )
|
||||||
|
yield $this->inputN+$this->neurons*($prev-1) + ($s-$offset-$neuron)/$this->neurons => $s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
**** Serialization ****
|
**** Serialization ****
|
||||||
************************************************/
|
************************************************/
|
||||||
|
@ -263,7 +393,7 @@
|
||||||
$csv = '';
|
$csv = '';
|
||||||
|
|
||||||
/* (2) Adds global attributes */
|
/* (2) Adds global attributes */
|
||||||
$csv .= $this->layers .','. $this->neurons .';';
|
$csv .= $this->layers .','. $this->inputN .','. $this->neurons .','. $this->outputN .';';
|
||||||
|
|
||||||
/* (3) Adds synapses data */
|
/* (3) Adds synapses data */
|
||||||
$csv .= implode(',', $this->synapses);
|
$csv .= implode(',', $this->synapses);
|
||||||
|
@ -271,7 +401,6 @@
|
||||||
return $csv;
|
return $csv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* BUILDS A Genome BASED ON HIS SERIALIZED REPRESENTATION
|
/* BUILDS A Genome BASED ON HIS SERIALIZED REPRESENTATION
|
||||||
*
|
*
|
||||||
* @serialized<String> Serialized representation of a Genome
|
* @serialized<String> Serialized representation of a Genome
|
||||||
|
@ -286,13 +415,15 @@
|
||||||
throw new \Error('Format error during Genome unserialization.');
|
throw new \Error('Format error during Genome unserialization.');
|
||||||
|
|
||||||
/* (2) Get global attributes */
|
/* (2) Get global attributes */
|
||||||
$global = explode(',', $segments[0]);
|
$globals = explode(',', $segments[0]);
|
||||||
|
|
||||||
if( count($global) < 2 )
|
if( count($globals) < 4 )
|
||||||
throw new \Error('Format error during Genome unserialization.');
|
throw new \Error('Format error during Genome unserialization.');
|
||||||
|
|
||||||
$this->layers = intval($global[0]);
|
$this->layers = intval($globals[0]);
|
||||||
$this->layers = intval($global[1]);
|
$this->inputN = intval($globals[1]);
|
||||||
|
$this->layers = intval($globals[2]);
|
||||||
|
$this->outputN = intval($globals[3]);
|
||||||
|
|
||||||
/* (3) Get synapses values */
|
/* (3) Get synapses values */
|
||||||
$this->synapses = explode(',', $segments[1]);
|
$this->synapses = explode(',', $segments[1]);
|
||||||
|
@ -310,7 +441,7 @@
|
||||||
if( $use_case ){
|
if( $use_case ){
|
||||||
|
|
||||||
/* (1) Basic Creation */
|
/* (1) Basic Creation */
|
||||||
$a = new Genome(2, 3); // 2 layers of 3 neurons each -> randomly filled
|
$a = new Genome(2, 3, 3, 2); // 2 layers of 3 neurons each -> randomly filled + 3 input neurons + 2 output neurons
|
||||||
|
|
||||||
/* (2) Inheritance */
|
/* (2) Inheritance */
|
||||||
$b = new Genome($a); // Clone of @a
|
$b = new Genome($a); // Clone of @a
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
private $numHid; // Number of hidden layer(s)
|
private $numHid; // Number of hidden layer(s)
|
||||||
private $numNeu; // Number of neurons for each hidden layer
|
private $numNeu; // Number of neurons for each hidden layer
|
||||||
|
|
||||||
private $max; // max values for input and output neurons
|
|
||||||
private $storage; // path to storage
|
private $storage; // path to storage
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
|
@ -221,63 +220,9 @@
|
||||||
**** Sample Setters ****
|
**** Sample Setters ****
|
||||||
************************************************/
|
************************************************/
|
||||||
|
|
||||||
/* SET SAMPLE INPUT/OUTPUT MAX VALUES
|
|
||||||
*
|
|
||||||
* @input<Array> Sample input-ordered-like max values
|
|
||||||
* @output<Array> Sample output-ordered-like max values
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function setMaxValues($input, $output){
|
|
||||||
if( !is_array($input) || !is_array($output) ) return;
|
|
||||||
|
|
||||||
$this->max = [
|
|
||||||
'input' => $input,
|
|
||||||
'output' => $output,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ADD SAMPLE TO THE NEURAL NETWORK
|
/* ADD SAMPLE TO THE NEURAL NETWORK
|
||||||
*
|
|
||||||
* @input<Array> Sample's input values
|
|
||||||
* @output<Array> Sample's output values
|
|
||||||
*
|
|
||||||
* @return added<Boolean> If sample has been added
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function addSample($input, $output){
|
public function addSample($input, $output){
|
||||||
/* [1] Check min/max values
|
|
||||||
=========================================================*/
|
|
||||||
/* (1) If not set yet */
|
|
||||||
if( is_null($this->max) )
|
|
||||||
throw new Error('You must use setMaxValues() before adding samples.');
|
|
||||||
|
|
||||||
/* (2) Checking each input */
|
|
||||||
foreach($input as $i=>$value){
|
|
||||||
// {2.1} If no max for this value //
|
|
||||||
if( !isset($this->max['input'][$i]) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// {2.2} Checks value //
|
|
||||||
if( $value > $this->max['input'][$i] )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (3) Checking each output */
|
|
||||||
foreach($output as $i=>$value){
|
|
||||||
// {2.1} If no max for this value //
|
|
||||||
if( !isset($this->max['output'][$i]) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// {2.2} Checks value //
|
|
||||||
if( $value > $this->max['output'][$i] )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* [2] Adds values
|
|
||||||
=========================================================*/
|
|
||||||
FileManager::append( $this->storage[1]['filename'], implode(',',$input) .';'. implode(',',$output) );
|
FileManager::append( $this->storage[1]['filename'], implode(',',$input) .';'. implode(',',$output) );
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -344,7 +289,7 @@
|
||||||
/* [2] Creates the neural network
|
/* [2] Creates the neural network
|
||||||
=========================================================*/
|
=========================================================*/
|
||||||
// best of last generation
|
// best of last generation
|
||||||
$best = new Genome($this->numHid, $this->numNeu);
|
$best = null;
|
||||||
|
|
||||||
|
|
||||||
/* (1) For each generation
|
/* (1) For each generation
|
||||||
|
@ -401,7 +346,6 @@
|
||||||
$json['numHid'] = $this->numHid;
|
$json['numHid'] = $this->numHid;
|
||||||
$json['numNeu'] = $this->numNeu;
|
$json['numNeu'] = $this->numNeu;
|
||||||
|
|
||||||
$json['max'] = $this->max;
|
|
||||||
$json['storage'] = $this->storage;
|
$json['storage'] = $this->storage;
|
||||||
|
|
||||||
/* (3) Returns serialized string */
|
/* (3) Returns serialized string */
|
||||||
|
@ -430,7 +374,6 @@
|
||||||
$this->numHid = $json['numHid'];
|
$this->numHid = $json['numHid'];
|
||||||
$this->numNeu = $json['numNeu'];
|
$this->numNeu = $json['numNeu'];
|
||||||
|
|
||||||
$this->max = $json['max'];
|
|
||||||
$this->storage = $json['storage'];
|
$this->storage = $json['storage'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace neuralnetwork\core;
|
|
||||||
|
|
||||||
use filemanager\core\FileManager;
|
|
||||||
|
|
||||||
class NeuralNetworkCore implements \Serializable{
|
|
||||||
|
|
||||||
/************************************************
|
|
||||||
**** GLOBAL ATTRIBUTES ****
|
|
||||||
************************************************/
|
|
||||||
private $maxGnr; // Maximum generation iterations
|
|
||||||
private $maxGnm; // Maximum genomes per generation
|
|
||||||
private $kptGnm; // Number of genomes kept for each generation
|
|
||||||
private $mutThr; // Mutation threshold
|
|
||||||
private $fitEnd; // Fitness range to end process
|
|
||||||
private $numHid; // Number of hidden layer(s)
|
|
||||||
private $numNeu; // Number of neurons for each hidden layer
|
|
||||||
|
|
||||||
private $max; // max values for input and output neurons
|
|
||||||
private $storage; // path to storage
|
|
||||||
|
|
||||||
/************************************************
|
|
||||||
**** LOCAL ATTRIBUTES ****
|
|
||||||
************************************************/
|
|
||||||
private $gnr; // Current generation index
|
|
||||||
private $gmns; // Current generation's genomes
|
|
||||||
private $gnm; // Current genome index
|
|
||||||
private $fit; // Current fitness
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
|
||||||
**** USE CASE ****
|
|
||||||
************************************************/
|
|
||||||
$use_case = false;
|
|
||||||
if( $use_case ){
|
|
||||||
|
|
||||||
/* (1) Instanciate and configure neural network
|
|
||||||
---------------------------------------------------------*/
|
|
||||||
/* (1) Initialize neural network to try 100 genomes over 50 generations (max) */
|
|
||||||
$neunet = NeuralNetwork::create(100, 50);
|
|
||||||
|
|
||||||
/* (2) Specifies that a fitness greater than 0.89 will stop the neural network */
|
|
||||||
$neunet->setFitnessEnd(0.89);
|
|
||||||
|
|
||||||
/* (3) Specifies that it will keep the 2 best genomes of each generation */
|
|
||||||
$neunet->setKeptGenomes(2);
|
|
||||||
|
|
||||||
/* (4) Specifies that for each mutation, it must be at maximum 30% different */
|
|
||||||
$neunet->setMutationThreshold(0.4);
|
|
||||||
|
|
||||||
/* (5) Specifies the number of hidden layers */
|
|
||||||
$neunet->setHiddenLayersCount(2);
|
|
||||||
|
|
||||||
/* (6) Specifies the number of neurons per hidden layer */
|
|
||||||
$neunet->setHiddenLayerNeuronsCount(4);
|
|
||||||
|
|
||||||
|
|
||||||
/* (2) Let's learn
|
|
||||||
---------------------------------------------------------*/
|
|
||||||
/* (1) Set dataset sample maximum values */
|
|
||||||
$neunet->setMaxValues([2,2,7], [10, 10]);
|
|
||||||
// input output
|
|
||||||
|
|
||||||
/* (2) Setting our example dataset */
|
|
||||||
$neunet->addSample([1.9, 2, 5.3], [6, 0]);
|
|
||||||
$neunet->addSample([1.2, 1.2, 0.1], [0, 2]);
|
|
||||||
$neunet->addSample([0, 0, 0], [5, 5]);
|
|
||||||
|
|
||||||
/* (3) Launch learning routine with callback function */
|
|
||||||
$neunet->learn(function($ctx){ // callback with @ctx the current context
|
|
||||||
echo 'generation '. $ctx['generation'] .'/'. $ctx['generations'] ."\n";
|
|
||||||
echo 'genome '. $ctx['genome'] .'/'. $ctx['genomes'] ."\n";
|
|
||||||
echo 'fitness: '. $ctx['fitness'] .'/'. $ctx['fitness_end']. "\n";
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/* (3) What to do next ?
|
|
||||||
---------------------------------------------------------*/
|
|
||||||
/* (1) Store your data */
|
|
||||||
$neunet->store('/some/path/to/storage');
|
|
||||||
|
|
||||||
/* (2) [HIDDEN] will create those 2 files */
|
|
||||||
'/some/path/to/storage.nn'; // containing neural network configuration
|
|
||||||
'/somt/path/to/storage.ln'; // containing neural network learnt values & weights
|
|
||||||
'/some/path/to/storage.ex'; // containing neural network samples
|
|
||||||
|
|
||||||
|
|
||||||
/* (4) And next ?
|
|
||||||
---------------------------------------------------------*/
|
|
||||||
/* (1) Load your stored neural network */
|
|
||||||
$trainednn = NeuralNetwork::load('/some/path/to/storage');
|
|
||||||
|
|
||||||
/* (2) Use it to guess output, with well-formed input */
|
|
||||||
$guessed = $trainednn->guess([1.2, 0.9, 6.1]);
|
|
||||||
|
|
||||||
/* (3) And correct it if needed*/
|
|
||||||
echo "1: ". $guessed[0] ."\n";
|
|
||||||
echo "2: ". $guessed[2] ."\n";
|
|
||||||
echo "is it correct ?\n";
|
|
||||||
// .. some code to manage and read correct output if it was wrong
|
|
||||||
$trainednn->addSample([1.2, 0.9, 6.1], [9, 0]);
|
|
||||||
|
|
||||||
// You can now relaunch a new neural network's learning
|
|
||||||
// or You can just add a few generations to evolve your neural network
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1 +0,0 @@
|
||||||
aa
|
|
|
@ -6,19 +6,18 @@
|
||||||
use \neuralnetwork\core\NeuralNetwork;
|
use \neuralnetwork\core\NeuralNetwork;
|
||||||
use \filemanager\core\FileManager;
|
use \filemanager\core\FileManager;
|
||||||
|
|
||||||
|
function behaviour($abc){
|
||||||
|
return [($abc[0] & $abc[1]), $abc[1] | $abc[2]];
|
||||||
|
}
|
||||||
|
|
||||||
if( false && 'test_creating_dataset' ){
|
if( false && 'test_creating_dataset' ){
|
||||||
|
|
||||||
function behaviour($abc){
|
|
||||||
return [($abc[0] & $abc[1]), $abc[1] | $abc[2]];
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Welcome to neural-network.php\n";
|
echo "Welcome to neural-network.php\n";
|
||||||
echo "-----------------------------\n";
|
echo "-----------------------------\n";
|
||||||
|
|
||||||
$nn = NeuralNetwork::create(50, 100);
|
$nn = NeuralNetwork::create(50, 100);
|
||||||
|
|
||||||
$nn->setMaxValues([1, 1, 1], [1, 1]);
|
|
||||||
$nn->setHiddenLayersCount(2);
|
$nn->setHiddenLayersCount(2);
|
||||||
$nn->setHiddenLayerNeuronsCount(3);
|
$nn->setHiddenLayerNeuronsCount(3);
|
||||||
|
|
||||||
|
@ -30,6 +29,14 @@
|
||||||
$d = [1, 0, 1]; $nn->addSample($d, behaviour($d));
|
$d = [1, 0, 1]; $nn->addSample($d, behaviour($d));
|
||||||
$d = [1, 1, 0]; $nn->addSample($d, behaviour($d));
|
$d = [1, 1, 0]; $nn->addSample($d, behaviour($d));
|
||||||
$d = [1, 1, 1]; $nn->addSample($d, behaviour($d));
|
$d = [1, 1, 1]; $nn->addSample($d, behaviour($d));
|
||||||
|
$d = [0, 0, 0]; $nn->addSample($d, behaviour($d));
|
||||||
|
$d = [0, 0, 2]; $nn->addSample($d, behaviour($d));
|
||||||
|
$d = [0, 2, 0]; $nn->addSample($d, behaviour($d));
|
||||||
|
$d = [0, 2, 2]; $nn->addSample($d, behaviour($d));
|
||||||
|
$d = [2, 0, 0]; $nn->addSample($d, behaviour($d));
|
||||||
|
$d = [2, 0, 2]; $nn->addSample($d, behaviour($d));
|
||||||
|
$d = [2, 2, 0]; $nn->addSample($d, behaviour($d));
|
||||||
|
$d = [2, 2, 2]; $nn->addSample($d, behaviour($d));
|
||||||
|
|
||||||
$nn->store('test/test1', true);
|
$nn->store('test/test1', true);
|
||||||
|
|
||||||
|
@ -67,9 +74,42 @@
|
||||||
|
|
||||||
if( true ){
|
if( true ){
|
||||||
|
|
||||||
$g = new Genome(2, 3);
|
$g = new Genome(2, 3, 3, 2);
|
||||||
|
$fitness = 0;
|
||||||
|
$g->setCallback(function($input, $output){
|
||||||
|
global $fitness;
|
||||||
|
echo "callback output: ".round($output[0]).", ".round($output[1])."\n";
|
||||||
|
$result = behaviour($input);
|
||||||
|
if( $output[0] == $result[0] )
|
||||||
|
$fitness++;
|
||||||
|
|
||||||
echo $g->process([1, 1, 0]);
|
if( $output[1] == $result[1] )
|
||||||
|
$fitness++;
|
||||||
|
});
|
||||||
|
|
||||||
|
echo $g->train([0, 0, 0]);
|
||||||
|
echo $g->train([0, 0, 1]);
|
||||||
|
echo $g->train([0, 1, 0]);
|
||||||
|
echo $g->train([0, 1, 1]);
|
||||||
|
echo $g->train([1, 0, 0]);
|
||||||
|
echo $g->train([1, 0, 1]);
|
||||||
|
echo $g->train([1, 1, 0]);
|
||||||
|
echo $g->train([1, 1, 1]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
echo $g->train([0, 0, 0]);
|
||||||
|
echo $g->train([0, 0, 2]);
|
||||||
|
echo $g->train([0, 2, 0]);
|
||||||
|
echo $g->train([0, 2, 2]);
|
||||||
|
echo $g->train([2, 0, 0]);
|
||||||
|
echo $g->train([2, 0, 2]);
|
||||||
|
echo $g->train([2, 2, 0]);
|
||||||
|
echo $g->train([2, 2, 2]);
|
||||||
|
|
||||||
|
echo "fitness: $fitness\n";
|
||||||
|
$g->setFitness($fitness);
|
||||||
|
echo $g->serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// REWRITE TEST
|
// REWRITE TEST
|
||||||
|
|
Loading…
Reference in New Issue