Created `Genome`'s `constructor` + finished `NeuralNetwork`'s `storage` and `attributes` management (+ serialization) + now it remains the hard & cool part -> genetic algorithm
This commit is contained in:
parent
e739508a45
commit
fd9bcefed7
|
@ -6,34 +6,49 @@
|
||||||
class FileManager{
|
class FileManager{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* CREATES A FILE WITH ITS NEEDED DIRECTORIES
|
||||||
|
*
|
||||||
|
* @path<String> Path of the needed file
|
||||||
|
*
|
||||||
|
* @return created<Boolean> If the file has been created successfully
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static function create($file){
|
||||||
|
/* (0) Checks arguments */
|
||||||
|
if( !is_string($file) || is_dir($file) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* (1) Creates file */
|
||||||
|
try{
|
||||||
|
fclose( fopen($file, 'w') );
|
||||||
|
return true;
|
||||||
|
}catch(Exception $e){ return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* READS FILE'S CONTENT
|
/* READS FILE'S CONTENT
|
||||||
*
|
*
|
||||||
* @file<String> File to read
|
* @file<String> File to read
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static function read($file){
|
public static function read($file){
|
||||||
/* [1] Init driver
|
/* (0) Checks arguments */
|
||||||
=========================================================*/
|
if( !is_string($file) )
|
||||||
|
throw new \Error('Wrong argument for read(<String>).');
|
||||||
|
|
||||||
|
/* (1) Initializing driver on file (read-flag) */
|
||||||
$driver = new \SplFileObject($file, 'r');
|
$driver = new \SplFileObject($file, 'r');
|
||||||
|
|
||||||
|
/* (2) Read lines */
|
||||||
/* [2] Read lines
|
|
||||||
=========================================================*/
|
|
||||||
$read = '';
|
$read = '';
|
||||||
|
|
||||||
/* (1) Reads lines */
|
|
||||||
$line = 0;
|
$line = 0;
|
||||||
while( $driver->current() ){
|
while( $driver->current() ){
|
||||||
$read .= "\n".$driver->current();
|
$read .= $driver->current();
|
||||||
$driver->next();
|
$driver->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* [3] Returns read content
|
|
||||||
=========================================================*/
|
|
||||||
/* (2) Destroy driver */
|
|
||||||
$driver = null;
|
|
||||||
|
|
||||||
/* (3) Returns result */
|
/* (3) Returns result */
|
||||||
return $read;
|
return $read;
|
||||||
}
|
}
|
||||||
|
@ -44,19 +59,27 @@
|
||||||
* @file<String> File to write to
|
* @file<String> File to write to
|
||||||
* @content<String> Content to write
|
* @content<String> Content to write
|
||||||
*
|
*
|
||||||
|
* @return written<Boolean> Returns if the content have been written successfully
|
||||||
|
*
|
||||||
|
* @note: Creates file if it is possible with `file_put_contents()`
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public static function write($file, $content){
|
public static function write($file, $content){
|
||||||
|
/* (0) Checks arguments */
|
||||||
|
if( !is_string($file) || !is_string($content) )
|
||||||
|
return false;
|
||||||
|
|
||||||
/* (1) Erase file */
|
/* (1) Erase file */
|
||||||
file_put_contents($file, '');
|
try{
|
||||||
|
fclose( fopen($file, 'w') );
|
||||||
|
}catch(Exception $e){ return false; }
|
||||||
|
|
||||||
/* (2) Get driver (write-flag) */
|
/* (2) Get driver (write-flag) */
|
||||||
$driver = new \SplFileObject($file, 'r+');
|
$driver = new \SplFileObject($file, 'r+');
|
||||||
|
|
||||||
/* (3) Writes content */
|
/* (3) Writes content */
|
||||||
$driver->fwrite($content);
|
return !is_null( $driver->fwrite($content) );
|
||||||
|
|
||||||
/* (4) Free driver */
|
|
||||||
$driver = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,16 +88,21 @@
|
||||||
* @file<String> File to append content to
|
* @file<String> File to append content to
|
||||||
* @content<String> Content to append
|
* @content<String> Content to append
|
||||||
*
|
*
|
||||||
|
* @return append<Boolean> Returns if the content have been append successfully
|
||||||
|
*
|
||||||
|
* @note: If file doesn't exists, returns false
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public static function append($file, $content){
|
public static function append($file, $content){
|
||||||
|
/* (0) Checks arguments */
|
||||||
|
if( !is_file($file) || !is_string($content) )
|
||||||
|
return false;
|
||||||
|
|
||||||
/* (1) Get driver (append-flag) */
|
/* (1) Get driver (append-flag) */
|
||||||
$driver = new \SplFileObject($file, 'a');
|
$driver = new \SplFileObject($file, 'a');
|
||||||
|
|
||||||
/* (2) append content */
|
/* (2) append content */
|
||||||
$driver->fwrite($content.PHP_EOL);
|
return !is_null( $driver->fwrite($content.PHP_EOL) );
|
||||||
|
|
||||||
/* (3) Free driver */
|
|
||||||
$driver = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace neuralnetwork\core;
|
||||||
|
|
||||||
|
use filemanager\core\FileManager;
|
||||||
|
|
||||||
|
class Genome/* implements \Serializable*/{
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
**** Constants ****
|
||||||
|
************************************************/
|
||||||
|
const MIN = 0;
|
||||||
|
const MAX = 1e9;
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
**** LOCAL ATTRIBUTES ****
|
||||||
|
************************************************/
|
||||||
|
private $layers; // Number of layers
|
||||||
|
private $neurons; // Neurons of the genome
|
||||||
|
private $synapses; // Synapses between neurons
|
||||||
|
|
||||||
|
|
||||||
|
/* CONSTRUCTOR
|
||||||
|
*
|
||||||
|
* -- RANDOM CREATION --
|
||||||
|
* @layers<int> Number of layers to manage
|
||||||
|
* @neurons<int> Number of neurons per layer
|
||||||
|
*
|
||||||
|
* -- CLONING --
|
||||||
|
* @base<Genom> Genome to clone to
|
||||||
|
*
|
||||||
|
* -- CROSS-OVER CREATION --
|
||||||
|
* @father<Genome> First parent
|
||||||
|
* @mother<Genome> Second parent
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct(){
|
||||||
|
/* (1) Get arguments */
|
||||||
|
$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 > 1 && abs(intval($argv[0])) === $argv[0] && abs(intval($argv[1])) === $argv[1] )
|
||||||
|
$this->construct_new($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.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BUILDS A Genome RANDOMLY WITH PARAMETERS
|
||||||
|
*
|
||||||
|
* @layers<int> The number of hidden layers to manage
|
||||||
|
* @neurons<int> The number neurons per layer
|
||||||
|
*
|
||||||
|
* @return created<Boolean> If Genome has been successfully created
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function construct_new($layers=-1, $neurons=-1){
|
||||||
|
/* (1) Checks parameters */
|
||||||
|
if( abs(intval($layers)) !== $layers || abs(intval($neurons)) !== $neurons )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// set layers
|
||||||
|
$this->layers = $layers;
|
||||||
|
|
||||||
|
/* (2) Creating random neurons */
|
||||||
|
$this->neurons = [];
|
||||||
|
for( $i = 0 ; $i < $neurons ; $i++ )
|
||||||
|
$this->neurons[$i] = rand(self::MIN, self::MAX) / self::MAX;
|
||||||
|
|
||||||
|
/* (3) Creating random synapses */
|
||||||
|
$this->synapses = [];
|
||||||
|
for( $i = 0, $l = pow($neurons, $layers) ; $i < $l ; $i++ )
|
||||||
|
$this->synapses[$i] = rand(self::MIN, self::MAX) / self::MAX;
|
||||||
|
|
||||||
|
// Success status
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BUILDS A Genome BASED ON A PARENT
|
||||||
|
*
|
||||||
|
* @parent<Genome> Parent genome to clone into children
|
||||||
|
*
|
||||||
|
* @return created<Boolean> If cloned Genome has been created successfully
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function construct_inheritance($parent=null){
|
||||||
|
/* (1) Checks parent type */
|
||||||
|
if( !($parent instanceof Genome) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* (2) Clones into this Genome */
|
||||||
|
$this->layers = $parent->layers;
|
||||||
|
$this->neurons = $parent->neurons;
|
||||||
|
$this->synapses = $parent->synapses;
|
||||||
|
|
||||||
|
// Success state
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BUILDS A Genome BASED ON TWO PARENTS
|
||||||
|
*
|
||||||
|
* @father<Genome> First parent ($father)
|
||||||
|
* @mother<Genome> Second parent ($mother)
|
||||||
|
*
|
||||||
|
* @return created<Boolean> If crossed-over Genome has been created successfully
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function construct_crossover($father=null, $mother=null){
|
||||||
|
/* (1) Checks parent type */
|
||||||
|
if( !($father instanceof Genome) || !($mother instanceof Genome) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* (2) Checks number of layers+neurons (same species) */
|
||||||
|
if( $father->layers !== $mother->layers || count($father->neurons) !== count($mother->neurons) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* (3) Do random crossover for neurons */
|
||||||
|
$this->neurons = [];
|
||||||
|
for( $i = 0, $l = count($father->neurons) ; $i < $l ; $i++ )
|
||||||
|
if( rand(0,1) ) $this->neurons[$i] = $father->neurons[$i];
|
||||||
|
else $this->neurons[$i] = $mother->neurons[$i];
|
||||||
|
|
||||||
|
/* (3) Creating random synapses */
|
||||||
|
$this->synapses = [];
|
||||||
|
for( $i = 0, $l = pow(count($this->neurons), $this->layers) ; $i < $l ; $i++ )
|
||||||
|
if( rand(0,1) ) $this->synapses[$i] = $father->synapses[$i];
|
||||||
|
else $this->synapses[$i] = $mother->synapses[$i];
|
||||||
|
|
||||||
|
// Success state
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
**** USE CASE ****
|
||||||
|
************************************************/
|
||||||
|
$use_case = false;
|
||||||
|
if( $use_case ){
|
||||||
|
|
||||||
|
/* (1) Basic Creation */
|
||||||
|
$a = new Genome(2, 3); // 2 layers of 3 neurons each -> randomly filled
|
||||||
|
|
||||||
|
/* (2) Inheritance */
|
||||||
|
$b = new Genome($a); // Clone of @a
|
||||||
|
|
||||||
|
/* (3) Section Title */
|
||||||
|
$b->mutation(0.3); // @b has now mutated with a threshold of 30%
|
||||||
|
|
||||||
|
/* (4) Cross-over (father+mother) */
|
||||||
|
$c = new Genome($a, $b); // @c is a randomly-done mix of @a and @b
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -3,39 +3,13 @@
|
||||||
namespace neuralnetwork\core;
|
namespace neuralnetwork\core;
|
||||||
|
|
||||||
use neuralnetwork\core\NeuralNetworkCore;
|
use neuralnetwork\core\NeuralNetworkCore;
|
||||||
|
use filemanager\core\FileManager;
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
**** Neural Network's Facade ****
|
**** Neural Network's Facade ****
|
||||||
************************************************/
|
************************************************/
|
||||||
class NeuralNetwork{
|
class NeuralNetwork{
|
||||||
|
|
||||||
/* CHECKS AND STORES PATHS TO STORAGE FILES
|
|
||||||
*
|
|
||||||
* @path<String> Path to storage
|
|
||||||
*
|
|
||||||
* @return files<Array> Array containing storage files' information
|
|
||||||
* @return error<NULL> Returns NULL if error
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public static function setStorage($path){
|
|
||||||
/* (1) Checks the path */
|
|
||||||
if( !preg_match("/^(?P<path>\/(?:[a-z0-9_-]+\/)*)(?P<filename>[a-z0-9_-]+)$/i", $path, $matches) )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
/* (2) Checks file's existence and returns it */
|
|
||||||
return [
|
|
||||||
[
|
|
||||||
'filename' => $matches['path'].$matches['filename'] .'.nn', // will contain neural network data
|
|
||||||
'exists' => is_file($matches['path'].$matches['filename'] .'.nn')
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'filename' => $matches['path'].$matches['filename'] .'.ex', // will contain samples
|
|
||||||
'exists' => is_file($matches['path'].$matches['filename'] .'.ex')
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CREATES A NEW NEURAL NETWORK
|
/* CREATES A NEW NEURAL NETWORK
|
||||||
*
|
*
|
||||||
* @generations<int> Maximum number of generations to process
|
* @generations<int> Maximum number of generations to process
|
||||||
|
@ -59,23 +33,19 @@
|
||||||
public static function load($storage=null){
|
public static function load($storage=null){
|
||||||
|
|
||||||
/* (1) Checks argument */
|
/* (1) Checks argument */
|
||||||
if( is_null($storage=self::setStorage($gnr_or_storage)) )
|
if( is_null($storage=NeuralNetworkCore::getStorage($storage)) )
|
||||||
throw new Error('Wrong NeuralNetwork loader\'s argument.');
|
throw new Error('Wrong NeuralNetwork loader\'s argument.');
|
||||||
|
|
||||||
/* (2) If files doesn't exist, raise error */
|
/* (2) If files doesn't exist, raise error */
|
||||||
if( !$storage[0]['exists'] || !$storage[1]['exists'] )
|
if( !$storage[0]['exists'] || !$storage[1]['exists'] || !$storage[2]['exists'] )
|
||||||
throw new Error('Loaded storage have file(s) missing.');
|
throw new Error('Loaded storage have file(s) missing.');
|
||||||
|
|
||||||
/* (3) Parses files */
|
/* (3) Unserialize last NeuralNetwork */
|
||||||
$last_network = json_decode( FileManager::read($storage[0]['filename']), true);
|
$last_network = FileManager::read($storage[0]['filename']);
|
||||||
|
|
||||||
/* (4) Creates and fill instance */
|
/* (4) Creates and fill instance */
|
||||||
$instance = new NeuralNetworkCore($last_network['generations'], $last_network['genomes']);
|
$instance = new NeuralNetworkCore(0, 0);
|
||||||
$instance->setStorage($gnr_or_storage);
|
$instance->unserialize($last_network);
|
||||||
$instance->setFitnessEnd($last_network['fitness_end']);
|
|
||||||
$instance->setKeptGenomes($last_network['kept_genomes']);
|
|
||||||
$instance->setMutationThreshold($last_network['mutation_threshold']);
|
|
||||||
$instance->setMaxValues($last_network['max_values']);
|
|
||||||
|
|
||||||
/* (5) Returns instance */
|
/* (5) Returns instance */
|
||||||
return $instance;
|
return $instance;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use filemanager\core\FileManager;
|
use filemanager\core\FileManager;
|
||||||
|
|
||||||
class NeuralNetworkCore{
|
class NeuralNetworkCore implements \Serializable{
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
**** GLOBAL ATTRIBUTES ****
|
**** GLOBAL ATTRIBUTES ****
|
||||||
|
@ -14,6 +14,8 @@
|
||||||
private $kptGnm; // Number of genomes kept for each generation
|
private $kptGnm; // Number of genomes kept for each generation
|
||||||
private $mutThr; // Mutation threshold
|
private $mutThr; // Mutation threshold
|
||||||
private $fitEnd; // Fitness range to end process
|
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 $max; // max values for input and output neurons
|
||||||
private $storage; // path to storage
|
private $storage; // path to storage
|
||||||
|
@ -28,6 +30,10 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
**** Static Methods ****
|
||||||
|
************************************************/
|
||||||
|
|
||||||
/* RETURNS CONFIGURATION CONTENT
|
/* RETURNS CONFIGURATION CONTENT
|
||||||
*
|
*
|
||||||
* @return config<Array> Associative array containing config content
|
* @return config<Array> Associative array containing config content
|
||||||
|
@ -48,6 +54,36 @@
|
||||||
return $parsed;
|
return $parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* RETURNS STORAGE STATUS & DATA
|
||||||
|
*
|
||||||
|
* @path<String> Path to storage
|
||||||
|
*
|
||||||
|
* @return files<Array> Array containing storage files' information
|
||||||
|
* @return error<NULL> Returns NULL if error
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static function getStorage($path){
|
||||||
|
/* (1) Checks the path */
|
||||||
|
if( !preg_match("/^(?P<path>(?:[a-z0-9_-]+\/)*)(?P<filename>[a-z0-9_-]+)$/i", $path, $matches) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$absolute_path = __ROOT__.self::conf()['storage_parent'].'/'.$matches['path'].$matches['filename'];
|
||||||
|
|
||||||
|
/* (2) Checks file's existence and returns it */
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'filename' => $absolute_path.'.nn', // will contain neural network data
|
||||||
|
'exists' => is_file($absolute_path.'.nn')
|
||||||
|
],[
|
||||||
|
'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')
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* CONSTRUCTOR
|
/* CONSTRUCTOR
|
||||||
*
|
*
|
||||||
|
@ -72,14 +108,17 @@
|
||||||
/* (2) Default attributes */
|
/* (2) Default attributes */
|
||||||
$default = self::conf()['default'];
|
$default = self::conf()['default'];
|
||||||
|
|
||||||
$this->setKeptGenomes($default['genomes_kept']); // default value
|
$this->setKeptGenomes($default['genomes_kept']); // default value
|
||||||
$this->setMutationThreshold($default['mutation_threshold']); // default value
|
$this->setMutationThreshold($default['mutation_threshold']); // default value
|
||||||
$this->setFitnessEnd($default['fitness_end']); // default value
|
$this->setFitnessEnd($default['fitness_end']); // default value
|
||||||
$this->max = null; // default value
|
$this->setHiddenLayersCount($default['hidden_layers']); // default value
|
||||||
$this->setStorage($default['storage']); // default value
|
$this->setHiddenLayerNeuronsCount($default['layer_neurons']); // default value
|
||||||
|
$this->max = null; // default value
|
||||||
|
$this->setStorage($default['storage']); // default value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
**** Attributes Setters ****
|
**** Attributes Setters ****
|
||||||
************************************************/
|
************************************************/
|
||||||
|
@ -92,26 +131,34 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function setStorage($path){
|
public function setStorage($path){
|
||||||
/* (1) Checks the path */
|
/* [1] Manages storage verification
|
||||||
if( !preg_match("/^(?P<path>(?:[a-z0-9_-]+\/)*)(?P<filename>[a-z0-9_-]+)$/i", $path, $matches) )
|
=========================================================*/
|
||||||
|
/* (1) Get storage files' information */
|
||||||
|
$storageData = self::getStorage($path);
|
||||||
|
|
||||||
|
/* (2) If doesn't exist, return FALSE */
|
||||||
|
if( is_null($storageData) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
$absolute_path = __ROOT__.self::conf()['storage_parent'].'/'.$matches['path'].$matches['filename'];
|
/* (3) Stores into instance */
|
||||||
|
$this->storage = $storageData;
|
||||||
|
|
||||||
/* (2) Checks file's existence and returns TRUE */
|
|
||||||
$this->storage = [
|
/* [2] Initializes files
|
||||||
[
|
=========================================================*/
|
||||||
'filename' => $absolute_path.'.nn', // will contain neural network data
|
/* (1) Creates directory/ies */
|
||||||
'exists' => is_file($absolute_path.'.nn')
|
if( !is_dir(dirname(($this->storage[0]['filename']))) )
|
||||||
],
|
mkdir( dirname($this->storage[0]['filename']), 0775, true );
|
||||||
[
|
|
||||||
'filename' => $absolute_path.'.ex', // will contain samples
|
// Checks
|
||||||
'exists' => is_file($absolute_path.'.ex')
|
if( !is_dir(dirname(($this->storage[0]['filename']))) )
|
||||||
]
|
throw new \Error('Error creating directory: '.dirname(($this->storage[0]['filename'])));
|
||||||
];
|
|
||||||
|
/* (2) Creates files */
|
||||||
|
foreach($this->storage as $file)
|
||||||
|
FileManager::create( $file['filename'] );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SET FITNESS END
|
/* SET FITNESS END
|
||||||
|
@ -147,6 +194,28 @@
|
||||||
$this->mutThr = $mutThr;
|
$this->mutThr = $mutThr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SET NUMBER OF NEURONS PER HIDDEN LAYER
|
||||||
|
*
|
||||||
|
* @numNeu<int> Number of Neurons per hidden layer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function setHiddenLayerNeuronsCount($numNeu){
|
||||||
|
if( abs(intval($numNeu)) !== $numNeu ) return;
|
||||||
|
|
||||||
|
$this->numNeu = $numNeu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SET NUMBER OF HIDDEN LAYERS
|
||||||
|
*
|
||||||
|
* @numHid<int> Number of hidden layer(s)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function setHiddenLayersCount($numHid){
|
||||||
|
if( abs(intval($numHid)) !== $numHid ) return;
|
||||||
|
|
||||||
|
$this->numHid = $numHid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
**** Sample Setters ****
|
**** Sample Setters ****
|
||||||
|
@ -167,7 +236,6 @@
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ADD SAMPLE TO THE NEURAL NETWORK
|
/* ADD SAMPLE TO THE NEURAL NETWORK
|
||||||
*
|
*
|
||||||
* @input<Array> Sample's input values
|
* @input<Array> Sample's input values
|
||||||
|
@ -205,7 +273,6 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* [2] Adds values
|
/* [2] Adds values
|
||||||
=========================================================*/
|
=========================================================*/
|
||||||
FileManager::append( $this->storage[1]['filename'], implode(',',$input) .';'. implode(',',$output) );
|
FileManager::append( $this->storage[1]['filename'], implode(',',$input) .';'. implode(',',$output) );
|
||||||
|
@ -213,6 +280,127 @@
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
**** Process Methods ****
|
||||||
|
************************************************/
|
||||||
|
|
||||||
|
/* DEFINES A STORAGE FOR THE NEURAL NETWORK
|
||||||
|
*
|
||||||
|
* @path<String> [OPTIONAL] Path to storage
|
||||||
|
* @override<Boolean> If override is wanted (will erase the whole previous data)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function store($path=null, $override=false){
|
||||||
|
if( !is_null($path) ){
|
||||||
|
|
||||||
|
/* [1] Checks storage existence
|
||||||
|
=========================================================*/
|
||||||
|
/* (1) Get storage files' information */
|
||||||
|
$storage = self::getStorage($path);
|
||||||
|
|
||||||
|
/* (2) Checks if files doesn't exist */
|
||||||
|
if( $storage[0]['exists'] || $storage[1]['exists'] || $storage[2]['exists'] )
|
||||||
|
if( !$override )
|
||||||
|
throw new \Error('This storage already exists, you can only load() it.');
|
||||||
|
|
||||||
|
|
||||||
|
/* [2] Creates storage & its files
|
||||||
|
=========================================================*/
|
||||||
|
/* (1) Keep last storage information */
|
||||||
|
$last_storage = $this->storage;
|
||||||
|
|
||||||
|
/* (2) Replaces with new storage */
|
||||||
|
$this->setStorage($path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* [3] Stores data
|
||||||
|
=========================================================*/
|
||||||
|
/* (1) Stores NeuralNetwork state (attributes) */
|
||||||
|
FileManager::write($storage[0]['filename'], $this->serialize());
|
||||||
|
|
||||||
|
/* (2) Stores samples */
|
||||||
|
FileManager::write($storage[1]['filename'], FileManager::read($last_storage[1]['filename']));
|
||||||
|
|
||||||
|
/* (3) Stores NeuralNetwork values & weights */
|
||||||
|
FileManager::write($storage[2]['filename'], FileManager::read($last_storage[2]['filename']));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* STARTS THE LEARNING ROUTINE
|
||||||
|
*
|
||||||
|
* @callback<Function> Callback function to display current state
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function learn($callback=null){
|
||||||
|
/* [1] Manages @callback argument
|
||||||
|
=========================================================*/
|
||||||
|
if( !is_callable($callback) )
|
||||||
|
$callback = function(){};
|
||||||
|
|
||||||
|
/* [2] Creates the neural network
|
||||||
|
=========================================================*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
**** Serialization Methods ****
|
||||||
|
************************************************/
|
||||||
|
|
||||||
|
/* RETURNS A SERIALIZED VERSION OF THE NeuralNetwork
|
||||||
|
*
|
||||||
|
* @return serialized<String> Serialized representation of the NeuralNetwork
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function serialize(){
|
||||||
|
/* (1) Initializes json */
|
||||||
|
$json = [];
|
||||||
|
|
||||||
|
/* (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['max'] = $this->max;
|
||||||
|
$json['storage'] = $this->storage;
|
||||||
|
|
||||||
|
/* (3) Returns serialized string */
|
||||||
|
return json_encode($json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BUILDS THE NeuralNetwork BASED ON ITS SERIALIZED STRING
|
||||||
|
*
|
||||||
|
* @serialized<String> Serialized representation of a NeuralNetwork
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function unserialize($serialized){
|
||||||
|
/* (1) Decodes json */
|
||||||
|
$json = json_decode($serialized, true);
|
||||||
|
|
||||||
|
/* (2) Manages json error */
|
||||||
|
if( !is_array($json) )
|
||||||
|
throw new Error('Unserialization format error.');
|
||||||
|
|
||||||
|
/* (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->max = $json['max'];
|
||||||
|
$this->storage = $json['storage'];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -238,6 +426,12 @@
|
||||||
/* (4) Specifies that for each mutation, it must be at maximum 30% different */
|
/* (4) Specifies that for each mutation, it must be at maximum 30% different */
|
||||||
$neunet->setMutationThreshold(0.4);
|
$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
|
/* (2) Let's learn
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
|
@ -261,17 +455,18 @@
|
||||||
/* (3) What to do next ?
|
/* (3) What to do next ?
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
/* (1) Store your data */
|
/* (1) Store your data */
|
||||||
$neunet->store('/some/path/to/filename');
|
$neunet->store('/some/path/to/storage');
|
||||||
|
|
||||||
/* (2) [HIDDEN] will create those 2 files */
|
/* (2) [HIDDEN] will create those 2 files */
|
||||||
'/some/path/to/filename.nn'; // containing neural network weights
|
'/some/path/to/storage.nn'; // containing neural network configuration
|
||||||
'/some/path/to/filename.ex'; // containing neural network samples
|
'/somt/path/to/storage.ln'; // containing neural network learnt values & weights
|
||||||
|
'/some/path/to/storage.ex'; // containing neural network samples
|
||||||
|
|
||||||
|
|
||||||
/* (4) And next ?
|
/* (4) And next ?
|
||||||
---------------------------------------------------------*/
|
---------------------------------------------------------*/
|
||||||
/* (1) Load your stored neural network */
|
/* (1) Load your stored neural network */
|
||||||
$trainednn = NeuralNetwork::load('/some/path/to/filename');
|
$trainednn = NeuralNetwork::load('/some/path/to/storage');
|
||||||
|
|
||||||
/* (2) Use it to guess output, with well-formed input */
|
/* (2) Use it to guess output, with well-formed input */
|
||||||
$guessed = $trainednn->guess([1.2, 0.9, 6.1]);
|
$guessed = $trainednn->guess([1.2, 0.9, 6.1]);
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
<?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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,8 @@
|
||||||
|
0,0,0;0
|
||||||
|
0,0,1;1
|
||||||
|
0,1,0;0
|
||||||
|
0,1,1;1
|
||||||
|
1,0,0;0
|
||||||
|
1,0,1;1
|
||||||
|
1,1,0;1
|
||||||
|
1,1,1;0
|
|
@ -0,0 +1 @@
|
||||||
|
{"maxGnr":50,"maxGnm":100,"kptGnm":2,"mutThr":0.3,"fitEnd":1,"numHid":2,"numNeu":3,"max":{"input":[1,1,1],"output":[1]},"storage":[{"filename":"\/home\/xdrm-brackets\/Desktop\/git.xdrm.io\/neural-network.php\/build\/neuralnetwork\/storage\/A\/B\/C\/test.nn","exists":true},{"filename":"\/home\/xdrm-brackets\/Desktop\/git.xdrm.io\/neural-network.php\/build\/neuralnetwork\/storage\/A\/B\/C\/test.ex","exists":true},{"filename":"\/home\/xdrm-brackets\/Desktop\/git.xdrm.io\/neural-network.php\/build\/neuralnetwork\/storage\/A\/B\/C\/test.ln","exists":true}]}
|
|
@ -5,6 +5,8 @@
|
||||||
"genomes_kept": 2,
|
"genomes_kept": 2,
|
||||||
"mutation_threshold": 0.3,
|
"mutation_threshold": 0.3,
|
||||||
"fitness_end": 1,
|
"fitness_end": 1,
|
||||||
"storage": "_buffer"
|
"storage": "_buffer",
|
||||||
|
"hidden_layers": 2,
|
||||||
|
"layer_neurons": 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,65 @@
|
||||||
|
|
||||||
require_once __ROOT__.'/autoloader.php';
|
require_once __ROOT__.'/autoloader.php';
|
||||||
|
|
||||||
|
use \neuralnetwork\core\Genome;
|
||||||
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[2]];
|
if( false && 'test_creating_dataset' ){
|
||||||
|
|
||||||
|
function behaviour($abc){
|
||||||
|
return [($abc[0] & $abc[1]) ^ $abc[2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Welcome to neural-network.php\n";
|
||||||
|
echo "-----------------------------\n";
|
||||||
|
|
||||||
|
$nn = NeuralNetwork::create(50, 100);
|
||||||
|
|
||||||
|
$nn->setMaxValues([1, 1, 1], [1]);
|
||||||
|
$nn->setHiddenLayersCount(2);
|
||||||
|
$nn->setHiddenLayerNeuronsCount(3);
|
||||||
|
|
||||||
|
$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));
|
||||||
|
|
||||||
|
$nn->store('A/B/C/test', true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "Welcome to neural-network.php\n";
|
if( false && 'load_neural_network' ){
|
||||||
echo "-----------------------------\n";
|
|
||||||
|
|
||||||
$nn = NeuralNetwork::create(50, 100);
|
$nn = NeuralNetwork::load('A/B/C/test');
|
||||||
|
|
||||||
$nn->setMaxValues([1, 1, 1], [1]);
|
}
|
||||||
|
|
||||||
$d = [0, 0, 0]; $nn->addSample($d, behaviour($d));
|
if( true && 'test_genomes' ){
|
||||||
$d = [0, 0, 1]; $nn->addSample($d, behaviour($d));
|
/* (1) Basic Creation */
|
||||||
$d = [0, 1, 0]; $nn->addSample($d, behaviour($d));
|
$a = new Genome(2, 3); // 2 layers of 3 neurons each -> randomly filled
|
||||||
$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));
|
|
||||||
|
|
||||||
|
/* (2) Inheritance */
|
||||||
|
$b = new Genome($a); // Clone of @a
|
||||||
|
|
||||||
|
/* (3) Section Title */
|
||||||
|
// $b->mutation(0.3); // @b has now mutated with a threshold of 30%
|
||||||
|
|
||||||
|
/* (4) Cross-over (father+mother) */
|
||||||
|
$c = new Genome($a, $b); // @c is a randomly-done mix of @a and @b
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// REWRITE TEST
|
||||||
|
// for( $a = 0, $al = 50 ; $a < $al ; $a++ )
|
||||||
|
// for( $b = 0, $bl = 20 ; $b < $bl ; $b++ ){
|
||||||
|
// print "genome $b/$bl on generation $a/$al \r";
|
||||||
|
// usleep(1000*10);
|
||||||
|
// }
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
Loading…
Reference in New Issue