Project structure + class bases and use cases for the Neural Network + FileManager to manage large files

This commit is contained in:
xdrm-brackets 2016-10-26 00:52:28 +02:00
commit 228d1d7913
9 changed files with 558 additions and 0 deletions

46
autoloader.php Normal file
View File

@ -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);
?>

View File

@ -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;
}
}
?>

View File

@ -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;
}
}
?>

View File

@ -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
}
?>

View File

@ -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

View File

View File

@ -0,0 +1,10 @@
{
"storage_parent": "/build/neuralnetwork/storage",
"default": {
"genomes_kept": 2,
"mutation_threshold": 0.3,
"fitness_end": 1,
"storage": "_buffer"
}
}

View File

@ -0,0 +1 @@
aa

29
public/main.php Normal file
View File

@ -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));
?>