Gestion de quasi-tout -> FONCTIONNELLLLLLL
This commit is contained in:
parent
e16048b569
commit
da50b03202
|
@ -23,7 +23,7 @@
|
|||
try{
|
||||
fclose( fopen($file, 'w') );
|
||||
return true;
|
||||
}catch(Exception $e){ return false; }
|
||||
}catch(\Exception $e){ return false; }
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
|||
public static function read($file){
|
||||
/* (0) Checks arguments */
|
||||
if( !is_string($file) )
|
||||
throw new \Error('Wrong argument for read(<String>).');
|
||||
throw new \Exception('Wrong argument for read(<String>).');
|
||||
|
||||
/* (1) Initializing driver on file (read-flag) */
|
||||
$driver = new \SplFileObject($file, 'r');
|
||||
|
@ -53,6 +53,54 @@
|
|||
return $read;
|
||||
}
|
||||
|
||||
/* READS A FILE'S SPECIFIC LINE
|
||||
*
|
||||
* @file<String> File to read
|
||||
* @line<int> Line to read
|
||||
*
|
||||
*/
|
||||
public static function readline($file, $line){
|
||||
/* (0) Checks arguments */
|
||||
if( !is_string($file) || intval($line) !== $line )
|
||||
throw new \Exception('Wrong argument for readline(<String>, <int>).');
|
||||
|
||||
/* (1) Initializing driver on file (read-flag) */
|
||||
$driver = new \SplFileObject($file, 'r');
|
||||
|
||||
/* (2) Goto specific line */
|
||||
$driver->seek($line);
|
||||
|
||||
/* (3) Return line's content */
|
||||
if( $driver->key() == $line )
|
||||
return $driver->current();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/* WRITES A FILE'S SPECIFIC LINE
|
||||
*
|
||||
* @file<String> File to read
|
||||
* @line<int> Line to read
|
||||
*
|
||||
*/
|
||||
public static function writeline($file, $line){
|
||||
/* (0) Checks arguments */
|
||||
if( !is_string($file) || intval($line) !== $line )
|
||||
throw new \Exception('Wrong argument for writeline(<String>, <int>).');
|
||||
|
||||
/* (1) Initializing driver on file (read-flag) */
|
||||
$driver = new \SplFileObject($file, 'r');
|
||||
|
||||
/* (2) Goto specific line */
|
||||
$driver->seek($line);
|
||||
|
||||
/* (3) Return line's content */
|
||||
if( $driver->key() == $line )
|
||||
return $driver->current();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* WRITES CONTENT TO A FILE
|
||||
*
|
||||
|
@ -72,7 +120,7 @@
|
|||
/* (1) Erase file */
|
||||
try{
|
||||
fclose( fopen($file, 'w') );
|
||||
}catch(Exception $e){ return false; }
|
||||
}catch(\Exception $e){ return false; }
|
||||
|
||||
/* (2) Get driver (write-flag) */
|
||||
$driver = new \SplFileObject($file, 'r+');
|
||||
|
|
|
@ -54,23 +54,24 @@
|
|||
$argv = func_get_args();
|
||||
$argc = count($argv);
|
||||
|
||||
|
||||
/* (2) If CrossoverCreation */
|
||||
if( $argc > 1 && $argv[0] instanceof Genome && $argv[1] instanceof Genome )
|
||||
$this->construct_crossover($argv[0], $argv[1]);
|
||||
|
||||
/* (3) If RandomCreation */
|
||||
else if( $argc > 3 && is_numeric($argv[0]) && is_numeric($argv[1]) && is_numeric($argv[2]) && is_numeric($argv[3]) )
|
||||
/* (2) If RandomCreation */
|
||||
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], $argv[2], $argv[3]);
|
||||
|
||||
/* (3) If CrossoverCreation */
|
||||
else if( $argc > 1 && $argv[0] instanceof Genome && $argv[1] instanceof Genome )
|
||||
$this->construct_crossover($argv[0], $argv[1]);
|
||||
|
||||
/* (4) If InheritanceCreation (clone) */
|
||||
else if( $argc > 0 && $argv[0] instanceof Genome )
|
||||
$this->construct_inheritance($argv[0]);
|
||||
|
||||
/* (5) If no match */
|
||||
else
|
||||
throw new \Error('Invalid Genome constructor\'s arguments.');
|
||||
throw new \Exception('Invalid Genome constructor\'s arguments.');
|
||||
|
||||
/* (6) Default values */
|
||||
$this->fitness = null;
|
||||
$this->callback = function(){};
|
||||
}
|
||||
|
||||
|
@ -159,7 +160,7 @@
|
|||
|
||||
/* (5) Do random crossover for synapses */
|
||||
$this->synapses = [];
|
||||
for( $i = 0, $l = $this->neurons*($this->inputN+$this->outputN+$this->neurons*$this->layers) ; $i < $l ; $i++ )
|
||||
for( $i = 0, $l = count($mother->synapses) ; $i < $l ; $i++ )
|
||||
if( !!rand(0,1) ) $this->synapses[$i] = $father->synapses[$i];
|
||||
else $this->synapses[$i] = $mother->synapses[$i];
|
||||
|
||||
|
@ -180,7 +181,7 @@
|
|||
public function setCallback($callback=null){
|
||||
/* (1) Checks @callback argument */
|
||||
if( !is_callable($callback) )
|
||||
throw new \Error('Wrong argument for Genome\'s callback function.');
|
||||
throw new \Exception('Wrong argument for Genome\'s callback function.');
|
||||
|
||||
/* (2) Set callback function */
|
||||
$this->callback = $callback;
|
||||
|
@ -194,13 +195,27 @@
|
|||
public function setFitness($fitness=null){
|
||||
/* (1) Checks @fitness argument */
|
||||
if( !is_numeric($fitness) )
|
||||
throw new \Error('Wrong argument for specifying Genome\'s fitness.');
|
||||
throw new \Exception('Wrong argument for specifying Genome\'s fitness.');
|
||||
|
||||
/* (2) Set fitness */
|
||||
$this->fitness = floatval($fitness);
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
**** Getter ****
|
||||
************************************************/
|
||||
|
||||
/* RETURNS THE FITNESS OF THE GENOME
|
||||
*
|
||||
* @return fitness<double> Current fitness (or NULL if not defined)
|
||||
*
|
||||
*/
|
||||
public function getFitness(){
|
||||
return $this->fitness;
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
**** Genome Actions ****
|
||||
************************************************/
|
||||
|
@ -213,10 +228,10 @@
|
|||
public function mutation($threshold=0.5){
|
||||
/* (1) Checks @threshold argument */
|
||||
if( floatval($threshold) !== $threshold || $threshold < 0 || $threshold > 1 )
|
||||
throw new \Error('Invalid threshold for Genome mutation.');
|
||||
throw new \Exception('Invalid threshold for Genome mutation.');
|
||||
|
||||
/* (2) Calculates how many neurons/synapses to mutate */
|
||||
$synapsesMutations = round( (count($this->synapses) - 1) * $threshold );
|
||||
$synapsesMutations = round( (count($this->synapses) - 1) * rand(0, $threshold*self::MAX)/self::MAX );
|
||||
|
||||
/* (3) Choose random synapses' indexes */
|
||||
$iSynapses = [];
|
||||
|
@ -243,7 +258,7 @@
|
|||
public function train($input=null, $callback=null){
|
||||
/* (1) Checks @input argument */
|
||||
if( !is_array($input) || count($input) !== $this->inputN )
|
||||
throw new \Error('Invalid @input for Genome\'s training.');
|
||||
throw new \Exception('Invalid @input for Genome\'s training.');
|
||||
|
||||
/* (2) Checks optional @callback argument */
|
||||
if( is_callable($callback) )
|
||||
|
@ -366,7 +381,7 @@
|
|||
|
||||
/* (3) If between hidden/output */
|
||||
}else if( $layer == $this->layers+1 ){
|
||||
$offset = $this->neurons * ($this->inputN+$this->neurons*$this->layers);
|
||||
$offset = $this->neurons * ($this->inputN+$this->neurons*($this->layers-1));
|
||||
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;
|
||||
|
||||
|
@ -408,21 +423,21 @@
|
|||
*/
|
||||
public function unserialize($serialized){
|
||||
/* (1) Segmenting data */
|
||||
$segments = explode(';', $serialized);
|
||||
$segments = explode(';', trim($serialized) );
|
||||
|
||||
// Manage segmentation error
|
||||
if( count($segments) < 3 )
|
||||
throw new \Error('Format error during Genome unserialization.');
|
||||
if( count($segments) < 2 )
|
||||
throw new \Exception('Format error during Genome unserialization.');
|
||||
|
||||
/* (2) Get global attributes */
|
||||
$globals = explode(',', $segments[0]);
|
||||
|
||||
if( count($globals) < 4 )
|
||||
throw new \Error('Format error during Genome unserialization.');
|
||||
throw new \Exception('Format error during Genome unserialization.');
|
||||
|
||||
$this->layers = intval($globals[0]);
|
||||
$this->inputN = intval($globals[1]);
|
||||
$this->layers = intval($globals[2]);
|
||||
$this->neurons = intval($globals[2]);
|
||||
$this->outputN = intval($globals[3]);
|
||||
|
||||
/* (3) Get synapses values */
|
||||
|
|
|
@ -34,14 +34,14 @@
|
|||
|
||||
/* (1) Checks argument */
|
||||
if( is_null($storage=NeuralNetworkCore::getStorage($storage)) )
|
||||
throw new Error('Wrong NeuralNetwork loader\'s argument.');
|
||||
throw new \Exception('Wrong NeuralNetwork loader\'s argument.');
|
||||
|
||||
/* (2) If files doesn't exist, raise error */
|
||||
if( !$storage[0]['exists'] || !$storage[1]['exists'] || !$storage[2]['exists'] )
|
||||
throw new Error('Loaded storage have file(s) missing.');
|
||||
if( !$storage['nn']['exists'] )
|
||||
throw new \Exception('Loaded storage have file(s) missing.');
|
||||
|
||||
/* (3) Unserialize last NeuralNetwork */
|
||||
$last_network = FileManager::read($storage[0]['filename']);
|
||||
$last_network = FileManager::read($storage['nn']['filename']);
|
||||
|
||||
/* (4) Creates and fill instance */
|
||||
$instance = new NeuralNetworkCore(0, 0);
|
||||
|
|
|
@ -11,21 +11,22 @@
|
|||
************************************************/
|
||||
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 $inpNeu; // Number of input neurons
|
||||
private $outNeu; // Number of output neurons
|
||||
|
||||
private $storage; // path to storage
|
||||
private $storage; // path to storage
|
||||
private $callback; // callback training function
|
||||
|
||||
/************************************************
|
||||
**** LOCAL ATTRIBUTES ****
|
||||
************************************************/
|
||||
private $gnr; // Current generation index
|
||||
private $gmns; // Current generation's genomes
|
||||
private $gnm; // Current genome index
|
||||
private $fit; // Current fitness
|
||||
public $gnr; // Current generation index
|
||||
public $gnm; // Current genome index
|
||||
private $genome; // Current genome instance
|
||||
|
||||
|
||||
|
||||
|
@ -70,15 +71,18 @@
|
|||
|
||||
/* (2) Checks file's existence and returns it */
|
||||
return [
|
||||
[
|
||||
'nn' => [
|
||||
'filename' => $absolute_path.'.nn', // will contain neural network data
|
||||
'exists' => is_file($absolute_path.'.nn')
|
||||
],[
|
||||
], 'ex' => [
|
||||
'filename' => $absolute_path.'.ex', // will contain samples
|
||||
'exists' => is_file($absolute_path.'.ex')
|
||||
],[
|
||||
'filename' => $absolute_path.'.ln', // will contain values & weights
|
||||
'exists' => is_file($absolute_path.'.ln')
|
||||
], 'gn' => [
|
||||
'filename' => $absolute_path.'.gn', // will contain genomes of the generation
|
||||
'exists' => is_file($absolute_path.'.gn')
|
||||
], 'ft' => [
|
||||
'filename' => $absolute_path.'.ft', // will contain genomes' fitness
|
||||
'exists' => is_file($absolute_path.'.ft')
|
||||
]
|
||||
];
|
||||
|
||||
|
@ -107,17 +111,16 @@
|
|||
/* (2) Default attributes */
|
||||
$default = self::conf()['default'];
|
||||
|
||||
$this->setKeptGenomes($default['genomes_kept']); // default value
|
||||
$this->setMutationThreshold($default['mutation_threshold']); // default value
|
||||
$this->setFitnessEnd($default['fitness_end']); // default value
|
||||
$this->setHiddenLayersCount($default['hidden_layers']); // default value
|
||||
$this->setHiddenLayerNeuronsCount($default['layer_neurons']); // default value
|
||||
$this->max = null; // default value
|
||||
$this->setStorage($default['storage']); // default value
|
||||
$this->callback = function(){}; // default value
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************
|
||||
**** Attributes Setters ****
|
||||
************************************************/
|
||||
|
@ -146,12 +149,12 @@
|
|||
/* [2] Initializes files
|
||||
=========================================================*/
|
||||
/* (1) Creates directory/ies */
|
||||
if( !is_dir(dirname(($this->storage[0]['filename']))) )
|
||||
mkdir( dirname($this->storage[0]['filename']), 0775, true );
|
||||
if( !is_dir(dirname(($this->storage['nn']['filename']))) )
|
||||
mkdir( dirname($this->storage['nn']['filename']), 0775, true );
|
||||
|
||||
// Checks
|
||||
if( !is_dir(dirname(($this->storage[0]['filename']))) )
|
||||
throw new \Error('Error creating directory: '.dirname(($this->storage[0]['filename'])));
|
||||
if( !is_dir(dirname(($this->storage['nn']['filename']))) )
|
||||
throw new \Exception('Error creating directory: '.dirname(($this->storage['nn']['filename'])));
|
||||
|
||||
/* (2) Creates files */
|
||||
foreach($this->storage as $file)
|
||||
|
@ -215,14 +218,49 @@
|
|||
$this->numHid = $numHid;
|
||||
}
|
||||
|
||||
/* SET NUMBER OF NEURONS OF THE INPUT LAYER
|
||||
*
|
||||
* @inpNeu<int> Number of neurons for the input layer
|
||||
*
|
||||
*/
|
||||
public function setInputLayerCount($inpNeu){
|
||||
if( abs(intval($inpNeu)) !== $inpNeu ) return;
|
||||
|
||||
$this->inpNeu = $inpNeu;
|
||||
}
|
||||
|
||||
/* SET NUMBER OF NEURONS OF THE OUTPUT LAYER
|
||||
*
|
||||
* @outNeu<int> Number of neurons for the output layer
|
||||
*
|
||||
*/
|
||||
public function setOutputLayerCount($outNeu){
|
||||
if( abs(intval($outNeu)) !== $outNeu ) return;
|
||||
|
||||
$this->outNeu = $outNeu;
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
**** Sample Setters ****
|
||||
************************************************/
|
||||
|
||||
/* ADD SAMPLE TO THE NEURAL NETWORK
|
||||
*
|
||||
* @input<Array> Set of input of the sample
|
||||
* @output<Array> Set of output of the sample
|
||||
*
|
||||
*/
|
||||
public function addSample($input, $output){
|
||||
FileManager::append( $this->storage[1]['filename'], implode(',',$input) .';'. implode(',',$output) );
|
||||
/* (1) Checks number of input neurons */
|
||||
if( !is_array($input) || is_null($this->inpNeu) || count($input) != $this->inpNeu )
|
||||
throw new \Exception('Wrong @input argument for addSample() method.');
|
||||
|
||||
/* (2) Checks number of output neurons */
|
||||
if( !is_array($output) || is_null($this->outNeu) || count($output) != $this->outNeu )
|
||||
throw new \Exception('Wrong @output argument for addSample() method.');
|
||||
|
||||
FileManager::append( $this->storage['ex']['filename'], implode(',',$input) .';'. implode(',',$output) );
|
||||
}
|
||||
|
||||
|
||||
|
@ -245,9 +283,9 @@
|
|||
$storage = self::getStorage($path);
|
||||
|
||||
/* (2) Checks if files doesn't exist */
|
||||
if( $storage[0]['exists'] || $storage[1]['exists'] || $storage[2]['exists'] )
|
||||
if( $storage['nn']['exists'] )
|
||||
if( !$override )
|
||||
throw new \Error('This storage already exists, you can only load() it.');
|
||||
throw new \Exception('This storage already exists, you can only load() it.');
|
||||
|
||||
|
||||
/* [2] Creates storage & its files
|
||||
|
@ -264,65 +302,185 @@
|
|||
/* [3] Stores data
|
||||
=========================================================*/
|
||||
/* (1) Stores NeuralNetwork state (attributes) */
|
||||
FileManager::write($storage[0]['filename'], $this->serialize());
|
||||
FileManager::write($storage['nn']['filename'], $this->serialize());
|
||||
|
||||
/* (2) Stores samples */
|
||||
FileManager::write($storage[1]['filename'], FileManager::read($last_storage[1]['filename']));
|
||||
FileManager::write($storage['ex']['filename'], FileManager::read($last_storage['ex']['filename']));
|
||||
|
||||
/* (3) Stores NeuralNetwork values & weights */
|
||||
FileManager::write($storage[2]['filename'], FileManager::read($last_storage[2]['filename']));
|
||||
FileManager::write($storage['gn']['filename'], FileManager::read($last_storage['gn']['filename']));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* STARTS THE LEARNING ROUTINE
|
||||
/* INITIALIZES THE LEARNING ROUTINE
|
||||
*
|
||||
* @callback<Function> Callback function to display current state
|
||||
*
|
||||
*/
|
||||
public function learn($callback=null){
|
||||
public function initLearningRoutine($callback=null){
|
||||
/* [1] Manages @callback argument
|
||||
=========================================================*/
|
||||
if( !is_callable($callback) )
|
||||
$callback = function(){};
|
||||
$this->callback = function(){};
|
||||
else
|
||||
$this->callback = $callback;
|
||||
|
||||
/* [2] Creates the neural network
|
||||
/* [2] Creates the First generation and serialize it
|
||||
=========================================================*/
|
||||
// best of last generation
|
||||
$best = null;
|
||||
/* (1) Initializes data & storage */
|
||||
$this->gnr = 0;
|
||||
$this->gnm = 0;
|
||||
FileManager::write($this->storage['gn']['filename'], '');
|
||||
FileManager::write($this->storage['ft']['filename'], '');
|
||||
|
||||
/* (2) Stores random genomes to storage */
|
||||
for( $g = 0 ; $g < $this->maxGnm ; $g++ ){
|
||||
$gnm = new Genome($this->numHid, $this->numNeu, $this->inpNeu, $this->outNeu);
|
||||
FileManager::append($this->storage['gn']['filename'], $gnm->serialize());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* RETURNS THE CURRENT GENOME
|
||||
*
|
||||
* @return genome<Genome> Returns the current genome
|
||||
*
|
||||
*/
|
||||
public function getGenome(){
|
||||
/* (1) Checks if learning routine has beem started */
|
||||
if( !is_numeric($this->gnm) )
|
||||
throw new \Exception('Learning routine closed.');
|
||||
|
||||
/* (2) Reads the current genome from storage */
|
||||
$serialized = FileManager::readline($this->storage['gn']['filename'], $this->gnm);
|
||||
|
||||
/* (3) Unserializes genome */
|
||||
$this->genome = new Genome(2, 2, 2, 2);
|
||||
|
||||
try{
|
||||
|
||||
$this->genome->unserialize($serialized);
|
||||
$this->genome->setCallback($this->callback);
|
||||
|
||||
return $this->genome;
|
||||
|
||||
/* (4) If error */
|
||||
}catch(Exception $e){ return null; }
|
||||
|
||||
}
|
||||
|
||||
/* ITERATES TO THE NEXT GENOME (IF SAME GENERATION)
|
||||
*
|
||||
*/
|
||||
public function nextGenome(){
|
||||
/* (0) Checks if Genome's fitness has been set
|
||||
---------------------------------------------------------*/
|
||||
if( !($this->genome instanceof Genome) || is_null($this->genome->getFitness()) )
|
||||
throw new \Exception('The learning routine is closed.');
|
||||
|
||||
/* (1) Stores fitness */
|
||||
FileManager::append($this->storage['ft']['filename'], strval($this->genome->getFitness()) );
|
||||
|
||||
|
||||
/* (1) For each generation
|
||||
---------------------------------------------------------*/
|
||||
for( $this->gnr = 0 ; $this->gnr < $this->maxGnr ; $this->gnr++ ){
|
||||
/* (1) Iterates if possible
|
||||
---------------------------------------------------------*/
|
||||
if( $this->gnm < $this->maxGnm-1 ){
|
||||
|
||||
/* (1) Initializes genome list for current generation */
|
||||
$this->gnms = [];
|
||||
$this->gnm++;
|
||||
|
||||
/* (2) For each genome of the generation */
|
||||
for( $this->gnm = 0 ; $this->gnm < $this->maxGnm ; $this->gnm++ ){
|
||||
/* (2) If must change generation
|
||||
---------------------------------------------------------*/
|
||||
}else if( $this->gnr < $this->maxGnr-1 ){
|
||||
|
||||
// {2.1} First Generation -> random genomes //
|
||||
if( $this->gnr === 0 )
|
||||
$this->gnms[$this->gnm] = new Genome($this->numHid, $this->numNeu);
|
||||
/* (1) Update generation & genome indexes */
|
||||
$this->gnr++;
|
||||
$this->gnm = 0;
|
||||
|
||||
// {2.2} Others generations -> crossover + mutation //
|
||||
else{
|
||||
$this->gnms[$this->gnm] = new Genome($best['father'], $best['mother']);
|
||||
$this->gnms[$this->gnm]->mutation($this->mutThr);
|
||||
}
|
||||
/* (2) Fetch the whole generation fitness values */
|
||||
$ftRead = FileManager::read($this->storage['ft']['filename']);
|
||||
$fitnesses = explode("\n", trim($ftRead) );
|
||||
|
||||
/* (3) Calculate fitness */
|
||||
// ... blablabla
|
||||
/* (3) Extract @mother & @father indexes */
|
||||
$iBest = $this->bestFitnesses($fitnesses);
|
||||
|
||||
/* (4) Extract best 2 genomes */
|
||||
$sFather = FileManager::readline($this->storage['gn']['filename'], $iBest[0]);
|
||||
$sMother = FileManager::readline($this->storage['gn']['filename'], $iBest[1]);
|
||||
|
||||
/* (5) Unserializes them */
|
||||
$father = new Genome(2, 2, 2, 2);
|
||||
$father->unserialize($sFather);
|
||||
|
||||
$mother = new Genome(2, 2, 2, 2);
|
||||
$mother->unserialize($sMother);
|
||||
|
||||
/* (6) Create new generation */
|
||||
FileManager::write($this->storage['gn']['filename'], '');
|
||||
FileManager::write($this->storage['ft']['filename'], '');
|
||||
|
||||
for( $g = 0 ; $g < $this->maxGnm ; $g++ ){
|
||||
|
||||
// {6.1} Re-use father //
|
||||
if( $g == 0 )
|
||||
FileManager::append($this->storage['gn']['filename'], $father->serialize());
|
||||
|
||||
// {6.2} Re-use mother //
|
||||
else if( $g == 1 )
|
||||
FileManager::append($this->storage['gn']['filename'], $mother->serialize());
|
||||
|
||||
// {6.3} Do cross-over + mutation for the rest //
|
||||
else{
|
||||
$gnm = new Genome($father, $mother);
|
||||
$gnm->mutation($this->mutThr);
|
||||
FileManager::append($this->storage['gn']['filename'], $gnm->serialize());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Select best genomes based on fitness
|
||||
$best = null;
|
||||
|
||||
/* (3) If end of process
|
||||
---------------------------------------------------------*/
|
||||
}else{
|
||||
$this->gnr = null;
|
||||
$this->gnm = null;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************
|
||||
**** Utility ****
|
||||
************************************************/
|
||||
|
||||
/* RETURNS THE 2 BEST FITNESSES FROM A LIST
|
||||
*
|
||||
* @fitnesses<Array> List of indexes fitnesses
|
||||
*
|
||||
* @return best<Array> List of the 2 indexes of the best fitnesses
|
||||
*
|
||||
*/
|
||||
private static function bestFitnesses($fitnesses=null){
|
||||
/* (1) Checks @fitnesses argument */
|
||||
if( !is_array($fitnesses) )
|
||||
throw new \Exception('Fitness list format error.');
|
||||
|
||||
/* (1) Find father (best)
|
||||
---------------------------------------------------------*/
|
||||
/* (1) Select greatest fitness value */
|
||||
arsort($fitnesses);
|
||||
|
||||
/* (2) Attributes @father & @mother indexes */
|
||||
$iFather = null;
|
||||
$iMother = null;
|
||||
$c = 0;
|
||||
foreach($fitnesses as $k=>$v){
|
||||
if( $c == 0 ) $iFather = $k;
|
||||
else if( $c == 1 ) $iMother = $k;
|
||||
else break;
|
||||
|
||||
$c++;
|
||||
}
|
||||
|
||||
return [ $iFather, $iMother ];
|
||||
}
|
||||
|
||||
/************************************************
|
||||
**** Serialization Methods ****
|
||||
|
@ -340,11 +498,12 @@
|
|||
/* (2) Adding attributes */
|
||||
$json['maxGnr'] = $this->maxGnr;
|
||||
$json['maxGnm'] = $this->maxGnm;
|
||||
$json['kptGnm'] = $this->kptGnm;
|
||||
$json['mutThr'] = $this->mutThr;
|
||||
$json['fitEnd'] = $this->fitEnd;
|
||||
$json['numHid'] = $this->numHid;
|
||||
$json['numNeu'] = $this->numNeu;
|
||||
$json['inpNeu'] = $this->inpNeu;
|
||||
$json['outNeu'] = $this->outNeu;
|
||||
|
||||
$json['storage'] = $this->storage;
|
||||
|
||||
|
@ -368,11 +527,12 @@
|
|||
/* (3) Attributing json attributes */
|
||||
$this->maxGnr = $json['maxGnr'];
|
||||
$this->maxGnm = $json['maxGnm'];
|
||||
$this->kptGnm = $json['kptGnm'];
|
||||
$this->mutThr = $json['mutThr'];
|
||||
$this->fitEnd = $json['fitEnd'];
|
||||
$this->numHid = $json['numHid'];
|
||||
$this->numNeu = $json['numNeu'];
|
||||
$this->inpNeu = $json['inpNeu'];
|
||||
$this->outNeu = $json['outNeu'];
|
||||
|
||||
$this->storage = $json['storage'];
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
0,0,0;0,0
|
||||
0,0,1;0,1
|
||||
0,1,0;0,1
|
||||
0,1,1;0,1
|
||||
1,0,0;0,0
|
||||
1,0,1;0,1
|
||||
1,1,0;1,1
|
||||
1,1,1;1,1
|
|
@ -6,3 +6,11 @@
|
|||
1,0,1;0,1
|
||||
1,1,0;1,1
|
||||
1,1,1;1,1
|
||||
0,0,0;0,0
|
||||
0,0,2;0,2
|
||||
0,2,0;0,2
|
||||
0,2,2;0,2
|
||||
2,0,0;0,0
|
||||
2,0,2;0,2
|
||||
2,2,0;2,2
|
||||
2,2,2;2,2
|
||||
|
|
|
@ -0,0 +1,520 @@
|
|||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
2
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
2
|
File diff suppressed because it is too large
Load Diff
|
@ -1 +1 @@
|
|||
{"maxGnr":50,"maxGnm":100,"kptGnm":2,"mutThr":0.3,"fitEnd":1,"numHid":2,"numNeu":3,"max":{"input":[1,1,1],"output":[1,1]},"storage":[{"filename":"\/home\/xdrm-brackets\/Desktop\/git.xdrm.io\/neural-network.php\/build\/neuralnetwork\/storage\/test\/test1.nn","exists":false},{"filename":"\/home\/xdrm-brackets\/Desktop\/git.xdrm.io\/neural-network.php\/build\/neuralnetwork\/storage\/test\/test1.ex","exists":false},{"filename":"\/home\/xdrm-brackets\/Desktop\/git.xdrm.io\/neural-network.php\/build\/neuralnetwork\/storage\/test\/test1.ln","exists":false}]}
|
||||
{"maxGnr":50,"maxGnm":1000,"kptGnm":2,"mutThr":0.3,"fitEnd":1,"numHid":2,"numNeu":3,"inpNeu":3,"outNeu":2,"storage":{"nn":{"filename":"\/home\/xdrm-brackets\/Desktop\/git.xdrm.io\/neural-network.php\/build\/neuralnetwork\/storage\/test\/test1.nn","exists":false},"ex":{"filename":"\/home\/xdrm-brackets\/Desktop\/git.xdrm.io\/neural-network.php\/build\/neuralnetwork\/storage\/test\/test1.ex","exists":false},"gn":{"filename":"\/home\/xdrm-brackets\/Desktop\/git.xdrm.io\/neural-network.php\/build\/neuralnetwork\/storage\/test\/test1.gn","exists":false},"ft":{"filename":"\/home\/xdrm-brackets\/Desktop\/git.xdrm.io\/neural-network.php\/build\/neuralnetwork\/storage\/test\/test1.ft","exists":false}}}
|
|
@ -2,7 +2,6 @@
|
|||
"storage_parent": "/build/neuralnetwork/storage",
|
||||
|
||||
"default": {
|
||||
"genomes_kept": 2,
|
||||
"mutation_threshold": 0.3,
|
||||
"fitness_end": 1,
|
||||
"storage": "_buffer",
|
||||
|
|
113
public/main.php
113
public/main.php
|
@ -10,38 +10,103 @@
|
|||
return [($abc[0] & $abc[1]), $abc[1] | $abc[2]];
|
||||
}
|
||||
|
||||
if( false && 'test_creating_dataset' ){
|
||||
if( true && 'test_creating_dataset' ){
|
||||
|
||||
$part = 1;
|
||||
|
||||
|
||||
echo "Welcome to neural-network.php\n";
|
||||
echo "-----------------------------\n";
|
||||
echo "-----------------------------\n\n";
|
||||
|
||||
$nn = NeuralNetwork::create(50, 100);
|
||||
/* [1] Trying to load neural network
|
||||
=========================================================*/
|
||||
try{
|
||||
|
||||
$nn->setHiddenLayersCount(2);
|
||||
$nn->setHiddenLayerNeuronsCount(3);
|
||||
$nn = NeuralNetwork::load('test/test1');
|
||||
echo "$part. NeuralNetwork loaded from 'test/test1'\n"; $part++;
|
||||
|
||||
$d = [0, 0, 0]; $nn->addSample($d, behaviour($d));
|
||||
$d = [0, 0, 1]; $nn->addSample($d, behaviour($d));
|
||||
$d = [0, 1, 0]; $nn->addSample($d, behaviour($d));
|
||||
$d = [0, 1, 1]; $nn->addSample($d, behaviour($d));
|
||||
$d = [1, 0, 0]; $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, 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));
|
||||
/* [2] Else, creates it
|
||||
=========================================================*/
|
||||
}catch(\Exception $e){
|
||||
|
||||
$nn = NeuralNetwork::create(50, 1000);
|
||||
|
||||
$nn->setHiddenLayersCount(4);
|
||||
$nn->setHiddenLayerNeuronsCount(3);
|
||||
$nn->setInputLayerCount(3);
|
||||
$nn->setOutputLayerCount(2);
|
||||
|
||||
echo "$part. NeuralNetwork configured\n"; $part++;
|
||||
|
||||
$d = [0, 0, 0]; $nn->addSample($d, behaviour($d));
|
||||
$d = [0, 0, 1]; $nn->addSample($d, behaviour($d));
|
||||
$d = [0, 1, 0]; $nn->addSample($d, behaviour($d));
|
||||
$d = [0, 1, 1]; $nn->addSample($d, behaviour($d));
|
||||
$d = [1, 0, 0]; $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, 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));
|
||||
echo "$part. Samples added to NeuralNetwork\n"; $part++;
|
||||
|
||||
$nn->store('test/test1', true);
|
||||
echo "$part. NeuralNetwork stored to 'test/test1'\n"; $part++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* [2] Initializing learning routine
|
||||
=========================================================*/
|
||||
$fitness = 0;
|
||||
$max_fit = 0;
|
||||
$nn->initLearningRoutine(function($input, $output){
|
||||
global $fitness;
|
||||
if( $output[0] == behaviour($input)[0] ) $fitness++;
|
||||
if( $output[1] == behaviour($input)[1] ) $fitness++;
|
||||
});
|
||||
echo "$part. Learning routine initialized.\n"; $part++;
|
||||
|
||||
|
||||
/* [3] Learning through generations and genomes
|
||||
=========================================================*/
|
||||
/* (1) For each generation */
|
||||
for( $gnr = 0 ; $gnr < 50 ; $gnr++ ){
|
||||
|
||||
/* (2) For each genome */
|
||||
for( $gnm = 0 ; $gnm < 1000 ; $gnm++ ){
|
||||
$fitness = 0;
|
||||
|
||||
/* (2.1) Get current genome */
|
||||
$g = $nn->getGenome();
|
||||
echo "\r[x] genome ".($nn->gnm+1)."/1000 on generation ".($nn->gnr+1)."/50 - max fitness: $max_fit ";
|
||||
|
||||
/* (2.2) Train genome with random samples */
|
||||
for( $r = 0 ; $r < 100 ; $r++ )
|
||||
$g->train([rand(0,10), rand(0,10), rand(0,10)]);
|
||||
|
||||
/* (2.3) Set fitness & go to next genome */
|
||||
if( $fitness > $max_fit ) $max_fit = $fitness;
|
||||
|
||||
$g->setFitness($fitness);
|
||||
$nn->nextGenome();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$nn->store('test/test1', true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if( false && 'load_neural_network' ){
|
||||
|
||||
$nn = NeuralNetwork::load('test/test1');
|
||||
|
@ -50,7 +115,7 @@
|
|||
|
||||
if( false && 'test_genomes' ){
|
||||
/* (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
|
||||
|
||||
echo "A : ".$a->serialize()."\n";
|
||||
|
||||
|
@ -72,7 +137,7 @@
|
|||
}
|
||||
|
||||
|
||||
if( true ){
|
||||
if( false ){
|
||||
|
||||
$g = new Genome(2, 3, 3, 2);
|
||||
$fitness = 0;
|
||||
|
|
Loading…
Reference in New Issue