Passed
Push — master ( 7ab80b...4af844 )
by Arkadiusz
03:30
created

MultilayerPerceptron::trainSamples()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Phpml\NeuralNetwork\Network;
6
7
use Phpml\Estimator;
8
use Phpml\Exception\InvalidArgumentException;
9
use Phpml\NeuralNetwork\Training\Backpropagation;
10
use Phpml\NeuralNetwork\ActivationFunction;
11
use Phpml\NeuralNetwork\Layer;
12
use Phpml\NeuralNetwork\Node\Bias;
13
use Phpml\NeuralNetwork\Node\Input;
14
use Phpml\NeuralNetwork\Node\Neuron;
15
use Phpml\NeuralNetwork\Node\Neuron\Synapse;
16
use Phpml\Helper\Predictable;
17
18
abstract class MultilayerPerceptron extends LayeredNetwork implements Estimator
19
{
20
    use Predictable;
21
22
    /**
23
     * @var array
24
     */
25
    protected $classes = [];
26
27
    /**
28
     * @var int
29
     */
30
    private $iterations;
31
32
    /**
33
     * @var Backpropagation
34
     */
35
    protected $backpropagation = null;
36
37
    /**
38
     * @param int                     $inputLayerFeatures
39
     * @param array                   $hiddenLayers
40
     * @param array                   $classes
41
     * @param int                     $iterations
42
     * @param ActivationFunction|null $activationFunction
43
     * @param int                     $theta
44
     *
45
     * @throws InvalidArgumentException
46
     */
47
    public function __construct(int $inputLayerFeatures, array $hiddenLayers, array $classes, int $iterations = 10000, ActivationFunction $activationFunction = null, int $theta = 1)
48
    {
49
        if (empty($hiddenLayers)) {
50
            throw InvalidArgumentException::invalidLayersNumber();
51
        }
52
53
        $nClasses = count($classes);
54
        if ($nClasses < 2) {
55
            throw InvalidArgumentException::invalidClassesNumber();
56
        }
57
        $this->classes = array_values($classes);
58
59
        $this->iterations = $iterations;
60
61
        $this->addInputLayer($inputLayerFeatures);
62
        $this->addNeuronLayers($hiddenLayers, $activationFunction);
63
        $this->addNeuronLayers([$nClasses], $activationFunction);
64
65
        $this->addBiasNodes();
66
        $this->generateSynapses();
67
68
        $this->backpropagation = new Backpropagation($theta);
69
    }
70
71
    /**
72
     * @param array $samples
73
     * @param array $targets
74
     */
75
    public function train(array $samples, array $targets)
76
    {
77
        for ($i = 0; $i < $this->iterations; ++$i) {
78
            $this->trainSamples($samples, $targets);
79
        }
80
    }
81
82
    /**
83
     * @param array $sample
84
     * @param mixed $target
85
     */
86
    protected abstract function trainSample(array $sample, $target);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
87
88
    /**
89
     * @param array $sample
90
     * @return mixed
91
     */
92
    protected abstract function predictSample(array $sample);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
93
94
    /**
95
     * @param int $nodes
96
     */
97
    private function addInputLayer(int $nodes)
98
    {
99
        $this->addLayer(new Layer($nodes, Input::class));
100
    }
101
102
    /**
103
     * @param array                   $layers
104
     * @param ActivationFunction|null $activationFunction
105
     */
106
    private function addNeuronLayers(array $layers, ActivationFunction $activationFunction = null)
107
    {
108
        foreach ($layers as $neurons) {
109
            $this->addLayer(new Layer($neurons, Neuron::class, $activationFunction));
110
        }
111
    }
112
113
    private function generateSynapses()
114
    {
115
        $layersNumber = count($this->layers) - 1;
116
        for ($i = 0; $i < $layersNumber; ++$i) {
117
            $currentLayer = $this->layers[$i];
118
            $nextLayer = $this->layers[$i + 1];
119
            $this->generateLayerSynapses($nextLayer, $currentLayer);
120
        }
121
    }
122
123
    private function addBiasNodes()
124
    {
125
        $biasLayers = count($this->layers) - 1;
126
        for ($i = 0; $i < $biasLayers; ++$i) {
127
            $this->layers[$i]->addNode(new Bias());
128
        }
129
    }
130
131
    /**
132
     * @param Layer $nextLayer
133
     * @param Layer $currentLayer
134
     */
135
    private function generateLayerSynapses(Layer $nextLayer, Layer $currentLayer)
136
    {
137
        foreach ($nextLayer->getNodes() as $nextNeuron) {
138
            if ($nextNeuron instanceof Neuron) {
139
                $this->generateNeuronSynapses($currentLayer, $nextNeuron);
140
            }
141
        }
142
    }
143
144
    /**
145
     * @param Layer  $currentLayer
146
     * @param Neuron $nextNeuron
147
     */
148
    private function generateNeuronSynapses(Layer $currentLayer, Neuron $nextNeuron)
149
    {
150
        foreach ($currentLayer->getNodes() as $currentNeuron) {
151
            $nextNeuron->addSynapse(new Synapse($currentNeuron));
152
        }
153
    }
154
155
    /**
156
     * @param array $samples
157
     * @param array $targets
158
     */
159
    private function trainSamples(array $samples, array $targets)
160
    {
161
        foreach ($targets as $key => $target) {
162
            $this->trainSample($samples[$key], $target);
163
        }
164
    }
165
}
166