From 4af8449b1c384a49a00b643f38ec20f5e20f5e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Monlla=C3=B3?= Date: Thu, 18 May 2017 06:07:14 +0800 Subject: [PATCH] Neural networks improvements (#89) * MultilayerPerceptron interface changes - Signature closer to other algorithms - New predict method - Remove desired error - Move maxIterations to constructor * MLP tests for multiple hidden layers and multi-class * Update all MLP-related tests * coding style fixes * Backpropagation included in multilayer-perceptron --- README.md | 3 +- docs/index.md | 3 +- .../neural-network/backpropagation.md | 30 ---- .../multilayer-perceptron-classifier.md | 50 +++++++ .../neural-network/multilayer-perceptron.md | 29 ---- mkdocs.yml | 3 +- src/Phpml/Classification/MLPClassifier.php | 67 +++++++++ .../Exception/InvalidArgumentException.php | 19 ++- .../NeuralNetwork/Network/LayeredNetwork.php | 2 +- .../Network/MultilayerPerceptron.php | 82 ++++++++++- src/Phpml/NeuralNetwork/Node/Neuron.php | 2 +- src/Phpml/NeuralNetwork/Training.php | 4 +- .../Training/Backpropagation.php | 107 ++++----------- src/Phpml/Regression/MLPRegressor.php | 80 ----------- .../Classification/MLPClassifierTest.php | 129 ++++++++++++++++++ .../Network/MultilayerPerceptronTest.php | 74 ---------- tests/Phpml/NeuralNetwork/Node/NeuronTest.php | 2 +- .../Training/BackpropagationTest.php | 30 ---- 18 files changed, 371 insertions(+), 345 deletions(-) delete mode 100644 docs/machine-learning/neural-network/backpropagation.md create mode 100644 docs/machine-learning/neural-network/multilayer-perceptron-classifier.md delete mode 100644 docs/machine-learning/neural-network/multilayer-perceptron.md create mode 100644 src/Phpml/Classification/MLPClassifier.php delete mode 100644 src/Phpml/Regression/MLPRegressor.php create mode 100644 tests/Phpml/Classification/MLPClassifierTest.php delete mode 100644 tests/Phpml/NeuralNetwork/Network/MultilayerPerceptronTest.php delete mode 100644 tests/Phpml/NeuralNetwork/Training/BackpropagationTest.php diff --git a/README.md b/README.md index b17ac72..48b7b48 100644 --- a/README.md +++ b/README.md @@ -76,8 +76,7 @@ Example scripts are available in a separate repository [php-ai/php-ml-examples]( * Workflow * [Pipeline](http://php-ml.readthedocs.io/en/latest/machine-learning/workflow/pipeline) * Neural Network - * [Multilayer Perceptron](http://php-ml.readthedocs.io/en/latest/machine-learning/neural-network/multilayer-perceptron/) - * [Backpropagation training](http://php-ml.readthedocs.io/en/latest/machine-learning/neural-network/backpropagation/) + * [Multilayer Perceptron Classifier](http://php-ml.readthedocs.io/en/latest/machine-learning/neural-network/multilayer-perceptron-classifier/) * Cross Validation * [Random Split](http://php-ml.readthedocs.io/en/latest/machine-learning/cross-validation/random-split/) * [Stratified Random Split](http://php-ml.readthedocs.io/en/latest/machine-learning/cross-validation/stratified-random-split/) diff --git a/docs/index.md b/docs/index.md index 156acb2..8d284af 100644 --- a/docs/index.md +++ b/docs/index.md @@ -65,8 +65,7 @@ Example scripts are available in a separate repository [php-ai/php-ml-examples]( * Workflow * [Pipeline](machine-learning/workflow/pipeline) * Neural Network - * [Multilayer Perceptron](machine-learning/neural-network/multilayer-perceptron/) - * [Backpropagation training](machine-learning/neural-network/backpropagation/) + * [Multilayer Perceptron Classifier](machine-learning/neural-network/multilayer-perceptron-classifier/) * Cross Validation * [Random Split](machine-learning/cross-validation/random-split/) * [Stratified Random Split](machine-learning/cross-validation/stratified-random-split/) diff --git a/docs/machine-learning/neural-network/backpropagation.md b/docs/machine-learning/neural-network/backpropagation.md deleted file mode 100644 index 0582351..0000000 --- a/docs/machine-learning/neural-network/backpropagation.md +++ /dev/null @@ -1,30 +0,0 @@ -# Backpropagation - -Backpropagation, an abbreviation for "backward propagation of errors", is a common method of training artificial neural networks used in conjunction with an optimization method such as gradient descent. - -## Constructor Parameters - -* $network (Network) - network to train (for example MultilayerPerceptron instance) -* $theta (int) - network theta parameter - -``` -use Phpml\NeuralNetwork\Network\MultilayerPerceptron; -use Phpml\NeuralNetwork\Training\Backpropagation; - -$network = new MultilayerPerceptron([2, 2, 1]); -$training = new Backpropagation($network); -``` - -## Training - -Example of XOR training: - -``` -$training->train( - $samples = [[1, 0], [0, 1], [1, 1], [0, 0]], - $targets = [[1], [1], [0], [0]], - $desiredError = 0.2, - $maxIteraions = 30000 -); -``` -You can train the neural network using multiple data sets, predictions will be based on all the training data. diff --git a/docs/machine-learning/neural-network/multilayer-perceptron-classifier.md b/docs/machine-learning/neural-network/multilayer-perceptron-classifier.md new file mode 100644 index 0000000..6f11f68 --- /dev/null +++ b/docs/machine-learning/neural-network/multilayer-perceptron-classifier.md @@ -0,0 +1,50 @@ +# MLPClassifier + +A multilayer perceptron (MLP) is a feedforward artificial neural network model that maps sets of input data onto a set of appropriate outputs. + +## Constructor Parameters + +* $inputLayerFeatures (int) - the number of input layer features +* $hiddenLayers (array) - array with the hidden layers configuration, each value represent number of neurons in each layers +* $classes (array) - array with the different training set classes (array keys are ignored) +* $iterations (int) - number of training iterations +* $theta (int) - network theta parameter +* $activationFunction (ActivationFunction) - neuron activation function + +``` +use Phpml\Classification\MLPClassifier; +$mlp = new MLPClassifier(4, [2], ['a', 'b', 'c']); + +// 4 nodes in input layer, 2 nodes in first hidden layer and 3 possible labels. + +``` + +## Train + +To train a MLP simply provide train samples and labels (as array). Example: + + +``` +$mlp->train( + $samples = [[1, 0, 0, 0], [0, 1, 1, 0], [1, 1, 1, 1], [0, 0, 0, 0]], + $targets = ['a', 'a', 'b', 'c'] +); + +``` + +## Predict + +To predict sample label use predict method. You can provide one sample or array of samples: + +``` +$mlp->predict([[1, 1, 1, 1], [0, 0, 0, 0]]); +// return ['b', 'c']; + +``` + +## Activation Functions + +* BinaryStep +* Gaussian +* HyperbolicTangent +* Sigmoid (default) diff --git a/docs/machine-learning/neural-network/multilayer-perceptron.md b/docs/machine-learning/neural-network/multilayer-perceptron.md deleted file mode 100644 index c1c0eef..0000000 --- a/docs/machine-learning/neural-network/multilayer-perceptron.md +++ /dev/null @@ -1,29 +0,0 @@ -# MultilayerPerceptron - -A multilayer perceptron (MLP) is a feedforward artificial neural network model that maps sets of input data onto a set of appropriate outputs. - -## Constructor Parameters - -* $layers (array) - array with layers configuration, each value represent number of neurons in each layers -* $activationFunction (ActivationFunction) - neuron activation function - -``` -use Phpml\NeuralNetwork\Network\MultilayerPerceptron; -$mlp = new MultilayerPerceptron([2, 2, 1]); - -// 2 nodes in input layer, 2 nodes in first hidden layer and 1 node in output layer -``` - -## Methods - -* setInput(array $input) -* getOutput() -* getLayers() -* addLayer(Layer $layer) - -## Activation Functions - -* BinaryStep -* Gaussian -* HyperbolicTangent -* Sigmoid (default) diff --git a/mkdocs.yml b/mkdocs.yml index 433cc3e..8c9c10c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,8 +21,7 @@ pages: - Workflow: - Pipeline: machine-learning/workflow/pipeline.md - Neural Network: - - Multilayer Perceptron: machine-learning/neural-network/multilayer-perceptron.md - - Backpropagation training: machine-learning/neural-network/backpropagation.md + - Multilayer Perceptron Classifier: machine-learning/neural-network/multilayer-perceptron-classifier.md - Cross Validation: - RandomSplit: machine-learning/cross-validation/random-split.md - Stratified Random Split: machine-learning/cross-validation/stratified-random-split.md diff --git a/src/Phpml/Classification/MLPClassifier.php b/src/Phpml/Classification/MLPClassifier.php new file mode 100644 index 0000000..c5d00bf --- /dev/null +++ b/src/Phpml/Classification/MLPClassifier.php @@ -0,0 +1,67 @@ +classes)) { + throw InvalidArgumentException::invalidTarget($target); + } + return array_search($target, $this->classes); + } + + /** + * @param array $sample + * + * @return mixed + */ + protected function predictSample(array $sample) + { + $output = $this->setInput($sample)->getOutput(); + + $predictedClass = null; + $max = 0; + foreach ($output as $class => $value) { + if ($value > $max) { + $predictedClass = $class; + $max = $value; + } + } + return $this->classes[$predictedClass]; + } + + /** + * @param array $sample + * @param mixed $target + */ + protected function trainSample(array $sample, $target) + { + + // Feed-forward. + $this->setInput($sample)->getOutput(); + + // Back-propagate. + $this->backpropagation->backpropagate($this->getLayers(), $this->getTargetClass($target)); + } +} diff --git a/src/Phpml/Exception/InvalidArgumentException.php b/src/Phpml/Exception/InvalidArgumentException.php index bf6d8cc..3e2bff5 100644 --- a/src/Phpml/Exception/InvalidArgumentException.php +++ b/src/Phpml/Exception/InvalidArgumentException.php @@ -66,6 +66,14 @@ class InvalidArgumentException extends \Exception return new self('Invalid clusters number'); } + /** + * @return InvalidArgumentException + */ + public static function invalidTarget($target) + { + return new self('Target with value ' . $target . ' is not part of the accepted classes'); + } + /** * @param string $language * @@ -89,6 +97,15 @@ class InvalidArgumentException extends \Exception */ public static function invalidLayersNumber() { - return new self('Provide at least 2 layers: 1 input and 1 output'); + return new self('Provide at least 1 hidden layer'); } + + /** + * @return InvalidArgumentException + */ + public static function invalidClassesNumber() + { + return new self('Provide at least 2 different classes'); + } + } diff --git a/src/Phpml/NeuralNetwork/Network/LayeredNetwork.php b/src/Phpml/NeuralNetwork/Network/LayeredNetwork.php index af2d723..cd90e3f 100644 --- a/src/Phpml/NeuralNetwork/Network/LayeredNetwork.php +++ b/src/Phpml/NeuralNetwork/Network/LayeredNetwork.php @@ -71,7 +71,7 @@ abstract class LayeredNetwork implements Network foreach ($this->getLayers() as $layer) { foreach ($layer->getNodes() as $node) { if ($node instanceof Neuron) { - $node->refresh(); + $node->reset(); } } } diff --git a/src/Phpml/NeuralNetwork/Network/MultilayerPerceptron.php b/src/Phpml/NeuralNetwork/Network/MultilayerPerceptron.php index 04664f9..5d7f94e 100644 --- a/src/Phpml/NeuralNetwork/Network/MultilayerPerceptron.php +++ b/src/Phpml/NeuralNetwork/Network/MultilayerPerceptron.php @@ -4,34 +4,93 @@ declare(strict_types=1); namespace Phpml\NeuralNetwork\Network; +use Phpml\Estimator; use Phpml\Exception\InvalidArgumentException; +use Phpml\NeuralNetwork\Training\Backpropagation; use Phpml\NeuralNetwork\ActivationFunction; use Phpml\NeuralNetwork\Layer; use Phpml\NeuralNetwork\Node\Bias; use Phpml\NeuralNetwork\Node\Input; use Phpml\NeuralNetwork\Node\Neuron; use Phpml\NeuralNetwork\Node\Neuron\Synapse; +use Phpml\Helper\Predictable; -class MultilayerPerceptron extends LayeredNetwork +abstract class MultilayerPerceptron extends LayeredNetwork implements Estimator { + use Predictable; + /** - * @param array $layers + * @var array + */ + protected $classes = []; + + /** + * @var int + */ + private $iterations; + + /** + * @var Backpropagation + */ + protected $backpropagation = null; + + /** + * @param int $inputLayerFeatures + * @param array $hiddenLayers + * @param array $classes + * @param int $iterations * @param ActivationFunction|null $activationFunction + * @param int $theta * * @throws InvalidArgumentException */ - public function __construct(array $layers, ActivationFunction $activationFunction = null) + public function __construct(int $inputLayerFeatures, array $hiddenLayers, array $classes, int $iterations = 10000, ActivationFunction $activationFunction = null, int $theta = 1) { - if (count($layers) < 2) { + if (empty($hiddenLayers)) { throw InvalidArgumentException::invalidLayersNumber(); } - $this->addInputLayer(array_shift($layers)); - $this->addNeuronLayers($layers, $activationFunction); + $nClasses = count($classes); + if ($nClasses < 2) { + throw InvalidArgumentException::invalidClassesNumber(); + } + $this->classes = array_values($classes); + + $this->iterations = $iterations; + + $this->addInputLayer($inputLayerFeatures); + $this->addNeuronLayers($hiddenLayers, $activationFunction); + $this->addNeuronLayers([$nClasses], $activationFunction); + $this->addBiasNodes(); $this->generateSynapses(); + + $this->backpropagation = new Backpropagation($theta); } + /** + * @param array $samples + * @param array $targets + */ + public function train(array $samples, array $targets) + { + for ($i = 0; $i < $this->iterations; ++$i) { + $this->trainSamples($samples, $targets); + } + } + + /** + * @param array $sample + * @param mixed $target + */ + protected abstract function trainSample(array $sample, $target); + + /** + * @param array $sample + * @return mixed + */ + protected abstract function predictSample(array $sample); + /** * @param int $nodes */ @@ -92,4 +151,15 @@ class MultilayerPerceptron extends LayeredNetwork $nextNeuron->addSynapse(new Synapse($currentNeuron)); } } + + /** + * @param array $samples + * @param array $targets + */ + private function trainSamples(array $samples, array $targets) + { + foreach ($targets as $key => $target) { + $this->trainSample($samples[$key], $target); + } + } } diff --git a/src/Phpml/NeuralNetwork/Node/Neuron.php b/src/Phpml/NeuralNetwork/Node/Neuron.php index 5194438..7c246be 100644 --- a/src/Phpml/NeuralNetwork/Node/Neuron.php +++ b/src/Phpml/NeuralNetwork/Node/Neuron.php @@ -68,7 +68,7 @@ class Neuron implements Node return $this->output; } - public function refresh() + public function reset() { $this->output = 0; } diff --git a/src/Phpml/NeuralNetwork/Training.php b/src/Phpml/NeuralNetwork/Training.php index d876af2..fcb6d73 100644 --- a/src/Phpml/NeuralNetwork/Training.php +++ b/src/Phpml/NeuralNetwork/Training.php @@ -9,8 +9,6 @@ interface Training /** * @param array $samples * @param array $targets - * @param float $desiredError - * @param int $maxIterations */ - public function train(array $samples, array $targets, float $desiredError = 0.001, int $maxIterations = 10000); + public function train(array $samples, array $targets); } diff --git a/src/Phpml/NeuralNetwork/Training/Backpropagation.php b/src/Phpml/NeuralNetwork/Training/Backpropagation.php index 43ac512..741db2b 100644 --- a/src/Phpml/NeuralNetwork/Training/Backpropagation.php +++ b/src/Phpml/NeuralNetwork/Training/Backpropagation.php @@ -4,18 +4,11 @@ declare(strict_types=1); namespace Phpml\NeuralNetwork\Training; -use Phpml\NeuralNetwork\Network; use Phpml\NeuralNetwork\Node\Neuron; -use Phpml\NeuralNetwork\Training; use Phpml\NeuralNetwork\Training\Backpropagation\Sigma; -class Backpropagation implements Training +class Backpropagation { - /** - * @var Network - */ - private $network; - /** * @var int */ @@ -27,96 +20,62 @@ class Backpropagation implements Training private $sigmas; /** - * @param Network $network - * @param int $theta + * @var array */ - public function __construct(Network $network, int $theta = 1) + private $prevSigmas; + + /** + * @param int $theta + */ + public function __construct(int $theta) { - $this->network = $network; $this->theta = $theta; } /** - * @param array $samples - * @param array $targets - * @param float $desiredError - * @param int $maxIterations + * @param array $layers + * @param mixed $targetClass */ - public function train(array $samples, array $targets, float $desiredError = 0.001, int $maxIterations = 10000) + public function backpropagate(array $layers, $targetClass) { - $samplesCount = count($samples); - for ($i = 0; $i < $maxIterations; ++$i) { - $resultsWithinError = $this->trainSamples($samples, $targets, $desiredError); - - if ($resultsWithinError === $samplesCount) { - break; - } - } - } - - /** - * @param array $samples - * @param array $targets - * @param float $desiredError - * - * @return int - */ - private function trainSamples(array $samples, array $targets, float $desiredError): int - { - $resultsWithinError = 0; - foreach ($targets as $key => $target) { - $result = $this->network->setInput($samples[$key])->getOutput(); - - if ($this->isResultWithinError($result, $target, $desiredError)) { - ++$resultsWithinError; - } else { - $this->trainSample($samples[$key], $target); - } - } - - return $resultsWithinError; - } - - /** - * @param array $sample - * @param array $target - */ - private function trainSample(array $sample, array $target) - { - $this->network->setInput($sample)->getOutput(); - $this->sigmas = []; - - $layers = $this->network->getLayers(); $layersNumber = count($layers); + // Backpropagation. for ($i = $layersNumber; $i > 1; --$i) { + $this->sigmas = []; foreach ($layers[$i - 1]->getNodes() as $key => $neuron) { + if ($neuron instanceof Neuron) { - $sigma = $this->getSigma($neuron, $target, $key, $i == $layersNumber); + $sigma = $this->getSigma($neuron, $targetClass, $key, $i == $layersNumber); foreach ($neuron->getSynapses() as $synapse) { $synapse->changeWeight($this->theta * $sigma * $synapse->getNode()->getOutput()); } } } + $this->prevSigmas = $this->sigmas; } } /** * @param Neuron $neuron - * @param array $target + * @param int $targetClass * @param int $key * @param bool $lastLayer * * @return float */ - private function getSigma(Neuron $neuron, array $target, int $key, bool $lastLayer): float + private function getSigma(Neuron $neuron, int $targetClass, int $key, bool $lastLayer): float { $neuronOutput = $neuron->getOutput(); $sigma = $neuronOutput * (1 - $neuronOutput); if ($lastLayer) { - $sigma *= ($target[$key] - $neuronOutput); + $value = 0; + if ($targetClass === $key) { + $value = 1; + } + $sigma *= ($value - $neuronOutput); } else { $sigma *= $this->getPrevSigma($neuron); } @@ -135,28 +94,10 @@ class Backpropagation implements Training { $sigma = 0.0; - foreach ($this->sigmas as $neuronSigma) { + foreach ($this->prevSigmas as $neuronSigma) { $sigma += $neuronSigma->getSigmaForNeuron($neuron); } return $sigma; } - - /** - * @param array $result - * @param array $target - * @param float $desiredError - * - * @return bool - */ - private function isResultWithinError(array $result, array $target, float $desiredError) - { - foreach ($target as $key => $value) { - if ($result[$key] > $value + $desiredError || $result[$key] < $value - $desiredError) { - return false; - } - } - - return true; - } } diff --git a/src/Phpml/Regression/MLPRegressor.php b/src/Phpml/Regression/MLPRegressor.php deleted file mode 100644 index 72e6a81..0000000 --- a/src/Phpml/Regression/MLPRegressor.php +++ /dev/null @@ -1,80 +0,0 @@ -hiddenLayers = $hiddenLayers; - $this->desiredError = $desiredError; - $this->maxIterations = $maxIterations; - $this->activationFunction = $activationFunction; - } - - /** - * @param array $samples - * @param array $targets - */ - public function train(array $samples, array $targets) - { - $layers = $this->hiddenLayers; - array_unshift($layers, count($samples[0])); - $layers[] = count($targets[0]); - - $this->perceptron = new MultilayerPerceptron($layers, $this->activationFunction); - - $trainer = new Backpropagation($this->perceptron); - $trainer->train($samples, $targets, $this->desiredError, $this->maxIterations); - } - - /** - * @param array $sample - * - * @return array - */ - protected function predictSample(array $sample) - { - return $this->perceptron->setInput($sample)->getOutput(); - } -} diff --git a/tests/Phpml/Classification/MLPClassifierTest.php b/tests/Phpml/Classification/MLPClassifierTest.php new file mode 100644 index 0000000..9f8b3fc --- /dev/null +++ b/tests/Phpml/Classification/MLPClassifierTest.php @@ -0,0 +1,129 @@ +assertCount(3, $mlp->getLayers()); + + $layers = $mlp->getLayers(); + + // input layer + $this->assertCount(3, $layers[0]->getNodes()); + $this->assertNotContainsOnly(Neuron::class, $layers[0]->getNodes()); + + // hidden layer + $this->assertCount(3, $layers[1]->getNodes()); + $this->assertNotContainsOnly(Neuron::class, $layers[1]->getNodes()); + + // output layer + $this->assertCount(2, $layers[2]->getNodes()); + $this->assertContainsOnly(Neuron::class, $layers[2]->getNodes()); + } + + public function testSynapsesGeneration() + { + $mlp = new MLPClassifier(2, [2], [0, 1]); + $layers = $mlp->getLayers(); + + foreach ($layers[1]->getNodes() as $node) { + if ($node instanceof Neuron) { + $synapses = $node->getSynapses(); + $this->assertCount(3, $synapses); + + $synapsesNodes = $this->getSynapsesNodes($synapses); + foreach ($layers[0]->getNodes() as $prevNode) { + $this->assertContains($prevNode, $synapsesNodes); + } + } + } + } + + public function testBackpropagationLearning() + { + // Single layer 2 classes. + $network = new MLPClassifier(2, [2], ['a', 'b'], 1000); + $network->train( + [[1, 0], [0, 1], [1, 1], [0, 0]], + ['a', 'b', 'a', 'b'] + ); + + $this->assertEquals('a', $network->predict([1, 0])); + $this->assertEquals('b', $network->predict([0, 1])); + $this->assertEquals('a', $network->predict([1, 1])); + $this->assertEquals('b', $network->predict([0, 0])); + } + + public function testBackpropagationLearningMultilayer() + { + // Multi-layer 2 classes. + $network = new MLPClassifier(5, [3, 2], ['a', 'b']); + $network->train( + [[1, 0, 0, 0, 0], [0, 1, 1, 0, 0], [1, 1, 1, 1, 1], [0, 0, 0, 0, 0]], + ['a', 'b', 'a', 'b'] + ); + + $this->assertEquals('a', $network->predict([1, 0, 0, 0, 0])); + $this->assertEquals('b', $network->predict([0, 1, 1, 0, 0])); + $this->assertEquals('a', $network->predict([1, 1, 1, 1, 1])); + $this->assertEquals('b', $network->predict([0, 0, 0, 0, 0])); + } + + public function testBackpropagationLearningMulticlass() + { + // Multi-layer more than 2 classes. + $network = new MLPClassifier(5, [3, 2], ['a', 'b', 4]); + $network->train( + [[1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 1, 0], [1, 1, 1, 1, 1], [0, 0, 0, 0, 0]], + ['a', 'b', 'a', 'a', 4] + ); + + $this->assertEquals('a', $network->predict([1, 0, 0, 0, 0])); + $this->assertEquals('b', $network->predict([0, 1, 0, 0, 0])); + $this->assertEquals('a', $network->predict([0, 0, 1, 1, 0])); + $this->assertEquals('a', $network->predict([1, 1, 1, 1, 1])); + $this->assertEquals(4, $network->predict([0, 0, 0, 0, 0])); + } + + /** + * @expectedException \Phpml\Exception\InvalidArgumentException + */ + public function testThrowExceptionOnInvalidLayersNumber() + { + new MLPClassifier(2, [], [0, 1]); + } + + /** + * @expectedException \Phpml\Exception\InvalidArgumentException + */ + public function testThrowExceptionOnInvalidClassesNumber() + { + new MLPClassifier(2, [2], [0]); + } + + /** + * @param array $synapses + * + * @return array + */ + private function getSynapsesNodes(array $synapses): array + { + $nodes = []; + foreach ($synapses as $synapse) { + $nodes[] = $synapse->getNode(); + } + + return $nodes; + } +} diff --git a/tests/Phpml/NeuralNetwork/Network/MultilayerPerceptronTest.php b/tests/Phpml/NeuralNetwork/Network/MultilayerPerceptronTest.php deleted file mode 100644 index e51d485..0000000 --- a/tests/Phpml/NeuralNetwork/Network/MultilayerPerceptronTest.php +++ /dev/null @@ -1,74 +0,0 @@ -assertCount(3, $mlp->getLayers()); - - $layers = $mlp->getLayers(); - - // input layer - $this->assertCount(3, $layers[0]->getNodes()); - $this->assertNotContainsOnly(Neuron::class, $layers[0]->getNodes()); - - // hidden layer - $this->assertCount(3, $layers[1]->getNodes()); - $this->assertNotContainsOnly(Neuron::class, $layers[0]->getNodes()); - - // output layer - $this->assertCount(1, $layers[2]->getNodes()); - $this->assertContainsOnly(Neuron::class, $layers[2]->getNodes()); - } - - public function testSynapsesGeneration() - { - $mlp = new MultilayerPerceptron([2, 2, 1]); - $layers = $mlp->getLayers(); - - foreach ($layers[1]->getNodes() as $node) { - if ($node instanceof Neuron) { - $synapses = $node->getSynapses(); - $this->assertCount(3, $synapses); - - $synapsesNodes = $this->getSynapsesNodes($synapses); - foreach ($layers[0]->getNodes() as $prevNode) { - $this->assertContains($prevNode, $synapsesNodes); - } - } - } - } - - /** - * @param array $synapses - * - * @return array - */ - private function getSynapsesNodes(array $synapses): array - { - $nodes = []; - foreach ($synapses as $synapse) { - $nodes[] = $synapse->getNode(); - } - - return $nodes; - } - - /** - * @expectedException \Phpml\Exception\InvalidArgumentException - */ - public function testThrowExceptionOnInvalidLayersNumber() - { - new MultilayerPerceptron([2]); - } -} diff --git a/tests/Phpml/NeuralNetwork/Node/NeuronTest.php b/tests/Phpml/NeuralNetwork/Node/NeuronTest.php index 6bd0398..a7bc6fe 100644 --- a/tests/Phpml/NeuralNetwork/Node/NeuronTest.php +++ b/tests/Phpml/NeuralNetwork/Node/NeuronTest.php @@ -46,7 +46,7 @@ class NeuronTest extends TestCase $this->assertEquals(0.5, $neuron->getOutput(), '', 0.01); - $neuron->refresh(); + $neuron->reset(); $this->assertEquals(0.88, $neuron->getOutput(), '', 0.01); } diff --git a/tests/Phpml/NeuralNetwork/Training/BackpropagationTest.php b/tests/Phpml/NeuralNetwork/Training/BackpropagationTest.php deleted file mode 100644 index a1915a3..0000000 --- a/tests/Phpml/NeuralNetwork/Training/BackpropagationTest.php +++ /dev/null @@ -1,30 +0,0 @@ -train( - [[1, 0], [0, 1], [1, 1], [0, 0]], - [[1], [1], [0], [0]], - $desiredError = 0.3, - 40000 - ); - - $this->assertEquals(0, $network->setInput([1, 1])->getOutput()[0], '', $desiredError); - $this->assertEquals(0, $network->setInput([0, 0])->getOutput()[0], '', $desiredError); - $this->assertEquals(1, $network->setInput([1, 0])->getOutput()[0], '', $desiredError); - $this->assertEquals(1, $network->setInput([0, 1])->getOutput()[0], '', $desiredError); - } -}