Project structure + class bases and use cases for the Neural Network + FileManager to manage large files
This commit is contained in:
commit
228d1d7913
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/* [1] On definit les chemins absolus si c'est pas deja fait
|
||||
=========================================================*/
|
||||
if( !defined('__ROOT__') ) define('__ROOT__', dirname(__FILE__) );
|
||||
if( !defined('__CONFIG__') ) define('__CONFIG__', __ROOT__.'/config' );
|
||||
if( !defined('__BUILD__') ) define('__BUILD__', __ROOT__.'/build' );
|
||||
if( !defined('__PUBLIC__') ) define('__PUBLIC__', __ROOT__.'/public_html' );
|
||||
|
||||
|
||||
/* ACTIVE LE DEBUGGAGE (WARNING + EXCEPTION)
|
||||
*
|
||||
*/
|
||||
function debug(){
|
||||
ini_set('display_errors',1);
|
||||
ini_set('display_startup_errors',1);
|
||||
error_reporting(-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* AUTOLOADER
|
||||
*
|
||||
* @className<String> 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);
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace filemanager\core;
|
||||
|
||||
|
||||
class FileManager{
|
||||
|
||||
|
||||
/* READS FILE'S CONTENT
|
||||
*
|
||||
* @file<String> 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<String> File to write to
|
||||
* @content<String> 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<String> File to append content to
|
||||
* @content<String> 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace neuralnetwork\core;
|
||||
|
||||
use neuralnetwork\core\NeuralNetworkCore;
|
||||
|
||||
/************************************************
|
||||
**** Neural Network's Facade ****
|
||||
************************************************/
|
||||
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
|
||||
*
|
||||
* @generations<int> Maximum number of generations to process
|
||||
* @genomes<int> Number of genomes per generation
|
||||
*
|
||||
* @return nn<NeuralNetwork> 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<String> Path to storage
|
||||
*
|
||||
* @return nn<NeuralNetwork> 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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,291 @@
|
|||
<?php
|
||||
|
||||
namespace neuralnetwork\core;
|
||||
|
||||
use filemanager\core\FileManager;
|
||||
|
||||
class NeuralNetworkCore{
|
||||
|
||||
/************************************************
|
||||
**** 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 $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
|
||||
|
||||
|
||||
|
||||
/* RETURNS CONFIGURATION CONTENT
|
||||
*
|
||||
* @return config<Array> 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<int> Maximum number of generations
|
||||
* @genomes<int> Maximum number of genomes per generation
|
||||
* OR
|
||||
* @storage<String> 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<String> Path to storage
|
||||
*
|
||||
* @return error<Boolean> Returns if paths are correct
|
||||
*
|
||||
*/
|
||||
public function setStorage($path){
|
||||
/* (1) Checks the path */
|
||||
if( !preg_match("/^(?P<path>(?:[a-z0-9_-]+\/)*)(?P<filename>[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<Double> 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<int> Number of kept genomes for each generation
|
||||
*
|
||||
*/
|
||||
public function setKeptGenomes($kptGnm){
|
||||
if( intval($kptGnm) !== $kptGnm ) return;
|
||||
|
||||
$this->kptGnm = $kptGnm;
|
||||
}
|
||||
|
||||
/* SET MUTATION THRESHOLD
|
||||
*
|
||||
* @mutThr<Double> Mutation threshold
|
||||
*
|
||||
*/
|
||||
public function setMutationThreshold($mutThr){
|
||||
if( abs(floatval($mutThr)) !== $mutThr ) return;
|
||||
|
||||
$this->mutThr = $mutThr;
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
**** 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
|
||||
*
|
||||
* @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){
|
||||
/* [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
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -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,10 @@
|
|||
{
|
||||
"storage_parent": "/build/neuralnetwork/storage",
|
||||
|
||||
"default": {
|
||||
"genomes_kept": 2,
|
||||
"mutation_threshold": 0.3,
|
||||
"fitness_end": 1,
|
||||
"storage": "_buffer"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
aa
|
|
@ -0,0 +1,29 @@
|
|||
<?php define('__ROOT__', dirname(dirname(__FILE__)) );
|
||||
|
||||
require_once __ROOT__.'/autoloader.php';
|
||||
|
||||
use \neuralnetwork\core\NeuralNetwork;
|
||||
use \filemanager\core\FileManager;
|
||||
|
||||
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]);
|
||||
|
||||
$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));
|
||||
|
||||
|
||||
?>
|
Loading…
Reference in New Issue