From 228d1d79133b0d0937c3888262e59698f3a3f095 Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Wed, 26 Oct 2016 00:52:28 +0200 Subject: [PATCH] Project structure + class bases and use cases for the Neural Network + FileManager to manage large files --- autoloader.php | 46 +++ build/filemanager/core/FileManager.php | 83 +++++ build/neuralnetwork/core/NeuralNetwork.php | 90 ++++++ .../neuralnetwork/core/NeuralNetworkCore.php | 291 ++++++++++++++++++ build/neuralnetwork/storage/_buffer.ex | 8 + build/neuralnetwork/storage/_buffer.nn | 0 config/neural-network.json | 10 + config/test_long_file.txt | 1 + public/main.php | 29 ++ 9 files changed, 558 insertions(+) create mode 100644 autoloader.php create mode 100644 build/filemanager/core/FileManager.php create mode 100644 build/neuralnetwork/core/NeuralNetwork.php create mode 100644 build/neuralnetwork/core/NeuralNetworkCore.php create mode 100644 build/neuralnetwork/storage/_buffer.ex create mode 100644 build/neuralnetwork/storage/_buffer.nn create mode 100644 config/neural-network.json create mode 100644 config/test_long_file.txt create mode 100644 public/main.php diff --git a/autoloader.php b/autoloader.php new file mode 100644 index 0000000..b17cf87 --- /dev/null +++ b/autoloader.php @@ -0,0 +1,46 @@ + Nom de la classe appelee + * + */ + function autoloader($className){ + $path = ''; + + /* [1] On utilise le namespace pour localiser + ===============================================*/ + // On remplace les '\' par des '/' + $path = str_replace('\\', '/', $className) . '.php'; + $path = __BUILD__.'/'.$path; + + // Si le fichier existe + if( file_exists($path) ) + require_once $path; // on inclue le fichier + + } + + // On definit l'autoloader comme autoloader (obvious) + spl_autoload_register('autoloader', false, true); + + +?> diff --git a/build/filemanager/core/FileManager.php b/build/filemanager/core/FileManager.php new file mode 100644 index 0000000..1f99f4e --- /dev/null +++ b/build/filemanager/core/FileManager.php @@ -0,0 +1,83 @@ + File to read + * + */ + public static function read($file){ + /* [1] Init driver + =========================================================*/ + $driver = new \SplFileObject($file, 'r'); + + + /* [2] Read lines + =========================================================*/ + $read = ''; + + /* (1) Reads lines */ + $line = 0; + while( $driver->current() ){ + $read .= "\n".$driver->current(); + $driver->next(); + } + + + /* [3] Returns read content + =========================================================*/ + /* (2) Destroy driver */ + $driver = null; + + /* (3) Returns result */ + return $read; + } + + + /* WRITES CONTENT TO A FILE + * + * @file File to write to + * @content Content to write + * + */ + public static function write($file, $content){ + /* (1) Erase file */ + file_put_contents($file, ''); + + /* (2) Get driver (write-flag) */ + $driver = new \SplFileObject($file, 'r+'); + + /* (3) Writes content */ + $driver->fwrite($content); + + /* (4) Free driver */ + $driver = null; + } + + + /* APPENDS CONTENT TO A FILE + * + * @file File to append content to + * @content Content to append + * + */ + public static function append($file, $content){ + /* (1) Get driver (append-flag) */ + $driver = new \SplFileObject($file, 'a'); + + /* (2) append content */ + $driver->fwrite($content.PHP_EOL); + + /* (3) Free driver */ + $driver = null; + } + + + } + +?> diff --git a/build/neuralnetwork/core/NeuralNetwork.php b/build/neuralnetwork/core/NeuralNetwork.php new file mode 100644 index 0000000..ae6cc62 --- /dev/null +++ b/build/neuralnetwork/core/NeuralNetwork.php @@ -0,0 +1,90 @@ + Path to storage + * + * @return files Array containing storage files' information + * @return error Returns NULL if error + * + */ + public static function setStorage($path){ + /* (1) Checks the path */ + if( !preg_match("/^(?P\/(?:[a-z0-9_-]+\/)*)(?P[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 + * + * @generations Maximum number of generations to process + * @genomes Number of genomes per generation + * + * @return nn New NeuralNetwork + * + */ + public static function create($generations=null, $genomes=null){ + /* Propagate instance creation */ + return new NeuralNetworkCore($generations, $genomes); + } + + /* LOADS A NEURAL NETWORK FROM STORAGE + * + * @storage Path to storage + * + * @return nn Loaded NeuralNetwork + * + */ + public static function load($storage=null){ + + /* (1) Checks argument */ + if( is_null($storage=self::setStorage($gnr_or_storage)) ) + throw new Error('Wrong NeuralNetwork loader\'s argument.'); + + /* (2) If files doesn't exist, raise error */ + if( !$storage[0]['exists'] || !$storage[1]['exists'] ) + throw new Error('Loaded storage have file(s) missing.'); + + /* (3) Parses files */ + $last_network = json_decode( FileManager::read($storage[0]['filename']), true); + + /* (4) Creates and fill instance */ + $instance = new NeuralNetworkCore($last_network['generations'], $last_network['genomes']); + $instance->setStorage($gnr_or_storage); + $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 */ + return $instance; + + } + + + } + + + +?> diff --git a/build/neuralnetwork/core/NeuralNetworkCore.php b/build/neuralnetwork/core/NeuralNetworkCore.php new file mode 100644 index 0000000..788bfe8 --- /dev/null +++ b/build/neuralnetwork/core/NeuralNetworkCore.php @@ -0,0 +1,291 @@ + Associative array containing config content + * + */ + public static function conf(){ + /* (1) Extract config file's content */ + $fcontent = FileManager::read(__CONFIG__.'/neural-network.json'); + + /* (2) Parse json */ + $parsed = json_decode($fcontent, true); + + // If json error -> return empty configuration + if( !is_array($parsed) ) + return []; + + /* (3) Return parsed data */ + return $parsed; + } + + + /* CONSTRUCTOR + * + * @generation Maximum number of generations + * @genomes Maximum number of genomes per generation + * OR + * @storage Storage location + * + */ + public function __construct($generations=null, $genomes=null){ + /* [1] If argument error + =========================================================*/ + if( intval($generations)!==$generations || intval($genomes) !== $genomes ) + throw new Error('Wrong constructor\'s arguments.'); + + /* [2] Initializing attributes + =========================================================*/ + /* (1) Set attributes */ + $this->maxGnr = intval($generations); + $this->maxGnm = intval($genomes); + + /* (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->max = null; // default value + $this->setStorage($default['storage']); // default value + } + + + /************************************************ + **** Attributes Setters **** + ************************************************/ + + /* CHECKS AND STORES PATHS TO STORAGE FILES + * + * @path Path to storage + * + * @return error Returns if paths are correct + * + */ + public function setStorage($path){ + /* (1) Checks the path */ + if( !preg_match("/^(?P(?:[a-z0-9_-]+\/)*)(?P[a-z0-9_-]+)$/i", $path, $matches) ) + return false; + + $absolute_path = __ROOT__.self::conf()['storage_parent'].'/'.$matches['path'].$matches['filename']; + + /* (2) Checks file's existence and returns TRUE */ + $this->storage = [ + [ + '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') + ] + ]; + + return true; + + } + + /* SET FITNESS END + * + * @fitEnd Fitness value to stop algorithm if reached + * + */ + public function setFitnessEnd($fitEnd){ + if( !is_numeric($fitEnd) ) return; + + $this->fitEnd = $fitEnd; + } + + /* SET THE NUMBER OF KEPT GENOMES FOR EACH GENERATION + * + * @kptGnm Number of kept genomes for each generation + * + */ + public function setKeptGenomes($kptGnm){ + if( intval($kptGnm) !== $kptGnm ) return; + + $this->kptGnm = $kptGnm; + } + + /* SET MUTATION THRESHOLD + * + * @mutThr Mutation threshold + * + */ + public function setMutationThreshold($mutThr){ + if( abs(floatval($mutThr)) !== $mutThr ) return; + + $this->mutThr = $mutThr; + } + + + /************************************************ + **** Sample Setters **** + ************************************************/ + + /* SET SAMPLE INPUT/OUTPUT MAX VALUES + * + * @input Sample input-ordered-like max values + * @output 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 + * + * @input Sample's input values + * @output Sample's output values + * + * @return added If sample has been added + * + */ + 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) ); + + return true; + } + + } + + + + + /************************************************ + **** 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); + + + /* (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/filename'); + + /* (2) [HIDDEN] will create those 2 files */ + '/some/path/to/filename.nn'; // containing neural network weights + '/some/path/to/filename.ex'; // containing neural network samples + + + /* (4) And next ? + ---------------------------------------------------------*/ + /* (1) Load your stored neural network */ + $trainednn = NeuralNetwork::load('/some/path/to/filename'); + + /* (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 + + } + +?> diff --git a/build/neuralnetwork/storage/_buffer.ex b/build/neuralnetwork/storage/_buffer.ex new file mode 100644 index 0000000..8496455 --- /dev/null +++ b/build/neuralnetwork/storage/_buffer.ex @@ -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 diff --git a/build/neuralnetwork/storage/_buffer.nn b/build/neuralnetwork/storage/_buffer.nn new file mode 100644 index 0000000..e69de29 diff --git a/config/neural-network.json b/config/neural-network.json new file mode 100644 index 0000000..4bf0df2 --- /dev/null +++ b/config/neural-network.json @@ -0,0 +1,10 @@ +{ + "storage_parent": "/build/neuralnetwork/storage", + + "default": { + "genomes_kept": 2, + "mutation_threshold": 0.3, + "fitness_end": 1, + "storage": "_buffer" + } +} diff --git a/config/test_long_file.txt b/config/test_long_file.txt new file mode 100644 index 0000000..7ec9a4b --- /dev/null +++ b/config/test_long_file.txt @@ -0,0 +1 @@ +aa \ No newline at end of file diff --git a/public/main.php b/public/main.php new file mode 100644 index 0000000..1a0ae39 --- /dev/null +++ b/public/main.php @@ -0,0 +1,29 @@ +setMaxValues([1, 1, 1], [1]); + + $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)); + + +?>