php-ml/src/NeuralNetwork/Training/Backpropagation.php

101 lines
2.4 KiB
PHP

<?php
declare(strict_types=1);
namespace Phpml\NeuralNetwork\Training;
use Phpml\NeuralNetwork\Node\Neuron;
use Phpml\NeuralNetwork\Training\Backpropagation\Sigma;
class Backpropagation
{
/**
* @var float
*/
private $learningRate;
/**
* @var array
*/
private $sigmas = [];
/**
* @var array
*/
private $prevSigmas = [];
public function __construct(float $learningRate)
{
$this->setLearningRate($learningRate);
}
public function setLearningRate(float $learningRate): void
{
$this->learningRate = $learningRate;
}
public function getLearningRate(): float
{
return $this->learningRate;
}
/**
* @param mixed $targetClass
*/
public function backpropagate(array $layers, $targetClass): void
{
$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, $targetClass, $key, $i == $layersNumber);
foreach ($neuron->getSynapses() as $synapse) {
$synapse->changeWeight($this->learningRate * $sigma * $synapse->getNode()->getOutput());
}
}
}
$this->prevSigmas = $this->sigmas;
}
// Clean some memory (also it helps make MLP persistency & children more maintainable).
$this->sigmas = [];
$this->prevSigmas = [];
}
private function getSigma(Neuron $neuron, int $targetClass, int $key, bool $lastLayer): float
{
$neuronOutput = $neuron->getOutput();
$sigma = $neuron->getDerivative();
if ($lastLayer) {
$value = 0;
if ($targetClass === $key) {
$value = 1;
}
$sigma *= ($value - $neuronOutput);
} else {
$sigma *= $this->getPrevSigma($neuron);
}
$this->sigmas[] = new Sigma($neuron, $sigma);
return $sigma;
}
private function getPrevSigma(Neuron $neuron): float
{
$sigma = 0.0;
foreach ($this->prevSigmas as $neuronSigma) {
$sigma += $neuronSigma->getSigmaForNeuron($neuron);
}
return $sigma;
}
}