Test Failed
Pull Request — master (#89)
by
unknown
03:29
created

MultilayerPerceptron::getTargetClass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Phpml\NeuralNetwork\Network;
6
7
use Phpml\Exception\InvalidArgumentException;
8
use Phpml\NeuralNetwork\ActivationFunction;
9
use Phpml\NeuralNetwork\Layer;
10
use Phpml\NeuralNetwork\Node\Bias;
11
use Phpml\NeuralNetwork\Node\Input;
12
use Phpml\NeuralNetwork\Node\Neuron;
13
use Phpml\NeuralNetwork\Node\Neuron\Synapse;
14
use Phpml\Helper\Predictable;
15
16
class MultilayerPerceptron extends LayeredNetwork
17
{
18
    use Predictable;
19
20
    /**
21
     * @var array
22
     */
23
    private $classes = [];
24
25
    /**
26
     * @param int                     $inputLayerFeatures
27
     * @param array                   $hiddenLayers
28
     * @param array                   $classes
29
     * @param ActivationFunction|null $activationFunction
30
     *
31
     * @throws InvalidArgumentException
32
     */
33
    public function __construct(int $inputLayerFeatures, array $hiddenLayers, array $classes, ActivationFunction $activationFunction = null)
34
    {
35
        if (count($inputLayerFeatures) < 1) {
36
            throw InvalidArgumentException::invalidLayersNumber();
37
        }
38
39
        $nClasses = count($classes);
40
        if ($nClasses < 2) {
41
            throw InvalidArgumentException::invalidClassesNumber();
42
        }
43
        $this->classes = $classes;
44
45
        $this->addInputLayer($inputLayerFeatures);
46
        $this->addNeuronLayers($hiddenLayers, $activationFunction);
47
        $this->addNeuronLayers([$nClasses], $activationFunction);
48
49
        $this->addBiasNodes();
50
        $this->generateSynapses();
51
    }
52
53
    /**
54
     * @param  mixed $target
55
     * @return int
56
     */
57
    public function getTargetClass($target): int
58
    {
59
        if (in_array($target, $this->classes) === false) {
60
            throw InvalidArgumentException::invalidTarget($target);
61
        }
62
        return array_search($target, $this->classes);
63
    }
64
65
    /**
66
     * @param array $sample
67
     *
68
     * @return mixed
69
     */
70
    public function predictSample(array $sample)
71
    {
72
        $output = $this->setInput($sample)->getOutput();
73
74
        $predictedClass = null;
75
        $max = 0;
76
        foreach ($output as $class => $value) {
77
            if ($value > $max) {
78
                $predictedClass = $class;
79
                $max = $value;
80
            }
81
        }
82
        return $this->classes[$predictedClass];
83
    }
84
85
    /**
86
     * @param int $nodes
87
     */
88
    private function addInputLayer(int $nodes)
89
    {
90
        $this->addLayer(new Layer($nodes, Input::class));
91
    }
92
93
    /**
94
     * @param array                   $layers
95
     * @param ActivationFunction|null $activationFunction
96
     */
97
    private function addNeuronLayers(array $layers, ActivationFunction $activationFunction = null)
98
    {
99
        foreach ($layers as $neurons) {
100
            $this->addLayer(new Layer($neurons, Neuron::class, $activationFunction));
101
        }
102
    }
103
104
    private function generateSynapses()
105
    {
106
        $layersNumber = count($this->layers) - 1;
107
        for ($i = 0; $i < $layersNumber; ++$i) {
108
            $currentLayer = $this->layers[$i];
109
            $nextLayer = $this->layers[$i + 1];
110
            $this->generateLayerSynapses($nextLayer, $currentLayer);
111
        }
112
    }
113
114
    private function addBiasNodes()
115
    {
116
        $biasLayers = count($this->layers) - 1;
117
        for ($i = 0; $i < $biasLayers; ++$i) {
118
            $this->layers[$i]->addNode(new Bias());
119
        }
120
    }
121
122
    /**
123
     * @param Layer $nextLayer
124
     * @param Layer $currentLayer
125
     */
126
    private function generateLayerSynapses(Layer $nextLayer, Layer $currentLayer)
127
    {
128
        foreach ($nextLayer->getNodes() as $nextNeuron) {
129
            if ($nextNeuron instanceof Neuron) {
130
                $this->generateNeuronSynapses($currentLayer, $nextNeuron);
131
            }
132
        }
133
    }
134
135
    /**
136
     * @param Layer  $currentLayer
137
     * @param Neuron $nextNeuron
138
     */
139
    private function generateNeuronSynapses(Layer $currentLayer, Neuron $nextNeuron)
140
    {
141
        foreach ($currentLayer->getNodes() as $currentNeuron) {
142
            $nextNeuron->addSynapse(new Synapse($currentNeuron));
143
        }
144
    }
145
}
146