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{
|
||||
|
||||
|
||||
|
||||
/* 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
|
||||
*
|
||||
* @file<String> File to read
|
||||
*
|
||||
*/
|
||||
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');
|
||||
|
||||
|
||||
/* [2] Read lines
|
||||
=========================================================*/
|
||||
/* (2) Read lines */
|
||||
$read = '';
|
||||
|
||||
/* (1) Reads lines */
|
||||
$line = 0;
|
||||
while( $driver->current() ){
|
||||
$read .= "\n".$driver->current();
|
||||
$read .= $driver->current();
|
||||
$driver->next();
|
||||
}
|
||||
|
||||
|
||||
/* [3] Returns read content
|
||||
=========================================================*/
|
||||
/* (2) Destroy driver */
|
||||
$driver = null;
|
||||
|
||||
/* (3) Returns result */
|
||||
return $read;
|
||||
}
|
||||
|
@ -44,19 +59,27 @@
|
|||
* @file<String> File to write to
|
||||
* @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){
|
||||
/* (0) Checks arguments */
|
||||
if( !is_string($file) || !is_string($content) )
|
||||
return false;
|
||||
|
||||
/* (1) Erase file */
|
||||
file_put_contents($file, '');
|
||||
try{
|
||||
fclose( fopen($file, 'w') );
|
||||
}catch(Exception $e){ return false; }
|
||||
|
||||
/* (2) Get driver (write-flag) */
|
||||
$driver = new \SplFileObject($file, 'r+');
|
||||
|
||||
/* (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
|
||||
* @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){
|
||||
/* (0) Checks arguments */
|
||||
if( !is_file($file) || !is_string($content) )
|
||||
return false;
|
||||
|
||||
/* (1) Get driver (append-flag) */
|
||||
$driver = new \SplFileObject($file, 'a');
|
||||
|
||||
/* (2) append content */
|
||||
$driver->fwrite($content.PHP_EOL);
|
||||
|
||||
/* (3) Free driver */
|
||||
$driver = null;
|
||||
return !is_null( $driver->fwrite($content.PHP_EOL) );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
use neuralnetwork\core\NeuralNetworkCore;
|
||||
use filemanager\core\FileManager;
|
||||
|
||||
/************************************************
|
||||
**** 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
|
||||
|
@ -59,23 +33,19 @@
|
|||
public static function load($storage=null){
|
||||
|
||||
/* (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.');
|
||||
|
||||
/* (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.');
|
||||
|
||||
/* (3) Parses files */
|
||||
$last_network = json_decode( FileManager::read($storage[0]['filename']), true);
|
||||
/* (3) Unserialize last NeuralNetwork */
|
||||
$last_network = FileManager::read($storage[0]['filename']);
|
||||
|
||||
/* (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']);
|
||||
$instance = new NeuralNetworkCore(0, 0);
|
||||
$instance->unserialize($last_network);
|
||||
|
||||
/* (5) Returns instance */
|
||||
return $instance;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use filemanager\core\FileManager;
|
||||
|
||||
class NeuralNetworkCore{
|
||||
class NeuralNetworkCore implements \Serializable{
|
||||
|
||||
/************************************************
|
||||
**** GLOBAL ATTRIBUTES ****
|
||||
|
@ -14,6 +14,8 @@
|
|||
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
|
||||
|
@ -28,6 +30,10 @@
|
|||
|
||||
|
||||
|
||||
/************************************************
|
||||
**** Static Methods ****
|
||||
************************************************/
|
||||
|
||||
/* RETURNS CONFIGURATION CONTENT
|
||||
*
|
||||
* @return config<Array> Associative array containing config content
|
||||
|
@ -48,6 +54,36 @@
|
|||
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
|
||||
*
|
||||
|
@ -72,14 +108,17 @@
|
|||
/* (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
|
||||
$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
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************
|
||||
**** Attributes Setters ****
|
||||
************************************************/
|
||||
|
@ -92,26 +131,34 @@
|
|||
*
|
||||
*/
|
||||
public function setStorage($path){
|
||||
/* (1) Checks the path */
|
||||
if( !preg_match("/^(?P<path>(?:[a-z0-9_-]+\/)*)(?P<filename>[a-z0-9_-]+)$/i", $path, $matches) )
|
||||
/* [1] Manages storage verification
|
||||
=========================================================*/
|
||||
/* (1) Get storage files' information */
|
||||
$storageData = self::getStorage($path);
|
||||
|
||||
/* (2) If doesn't exist, return FALSE */
|
||||
if( is_null($storageData) )
|
||||
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 = [
|
||||
[
|
||||
'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')
|
||||
]
|
||||
];
|
||||
|
||||
/* [2] Initializes files
|
||||
=========================================================*/
|
||||
/* (1) Creates directory/ies */
|
||||
if( !is_dir(dirname(($this->storage[0]['filename']))) )
|
||||
mkdir( dirname($this->storage[0]['filename']), 0775, true );
|
||||
|
||||
// Checks
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/* SET FITNESS END
|
||||
|
@ -147,6 +194,28 @@
|
|||
$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 ****
|
||||
|
@ -167,7 +236,6 @@
|
|||
];
|
||||
}
|
||||
|
||||
|
||||
/* ADD SAMPLE TO THE NEURAL NETWORK
|
||||
*
|
||||
* @input<Array> Sample's input values
|
||||
|
@ -205,7 +273,6 @@
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* [2] Adds values
|
||||
=========================================================*/
|
||||
FileManager::append( $this->storage[1]['filename'], implode(',',$input) .';'. implode(',',$output) );
|
||||
|
@ -213,6 +280,127 @@
|
|||
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 */
|
||||
$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
|
||||
---------------------------------------------------------*/
|
||||
|
@ -261,17 +455,18 @@
|
|||
/* (3) What to do next ?
|
||||
---------------------------------------------------------*/
|
||||
/* (1) Store your data */
|
||||
$neunet->store('/some/path/to/filename');
|
||||
$neunet->store('/some/path/to/storage');
|
||||
|
||||
/* (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
|
||||
'/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/filename');
|
||||
$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]);
|
||||
|
|
|
@ -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,
|
||||
"mutation_threshold": 0.3,
|
||||
"fitness_end": 1,
|
||||
"storage": "_buffer"
|
||||
"storage": "_buffer",
|
||||
"hidden_layers": 2,
|
||||
"layer_neurons": 3
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,28 +2,65 @@
|
|||
|
||||
require_once __ROOT__.'/autoloader.php';
|
||||
|
||||
use \neuralnetwork\core\Genome;
|
||||
use \neuralnetwork\core\NeuralNetwork;
|
||||
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";
|
||||
echo "-----------------------------\n";
|
||||
if( false && 'load_neural_network' ){
|
||||
|
||||
$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));
|
||||
$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));
|
||||
if( true && 'test_genomes' ){
|
||||
/* (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
|
||||
}
|
||||
|
||||
|
||||
// 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