Passed
Push — master ( 331d4b...653c7c )
by Arkadiusz
02:19
created

src/Phpml/Helper/Optimizer/GD.php (7 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Phpml\Helper\Optimizer;
6
7
/**
8
 * Batch version of Gradient Descent to optimize the weights
9
 * of a classifier given samples, targets and the objective function to minimize
10
 */
11
class GD extends StochasticGD
12
{
13
    /**
14
     * Number of samples given
15
     *
16
     * @var int
17
     */
18
    protected $sampleCount = null;
19
20
    public function runOptimization(array $samples, array $targets, \Closure $gradientCb) : array
21
    {
22
        $this->samples = $samples;
23
        $this->targets = $targets;
24
        $this->gradientCb = $gradientCb;
25
        $this->sampleCount = count($this->samples);
26
27
        // Batch learning is executed:
28
        $currIter = 0;
29
        $this->costValues = [];
30
        while ($this->maxIterations > $currIter++) {
31
            $theta = $this->theta;
32
33
            // Calculate update terms for each sample
34
            [$errors, $updates, $totalPenalty] = $this->gradient($theta);
0 ignored issues
show
The variable $errors does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
The variable $updates does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
The variable $totalPenalty does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
35
36
            $this->updateWeightsWithUpdates($updates, $totalPenalty);
37
38
            $this->costValues[] = array_sum($errors) / $this->sampleCount;
39
40
            if ($this->earlyStop($theta)) {
41
                break;
42
            }
43
        }
44
45
        $this->clear();
46
47
        return $this->theta;
48
    }
49
50
    /**
51
     * Calculates gradient, cost function and penalty term for each sample
52
     * then returns them as an array of values
53
     */
54
    protected function gradient(array $theta) : array
55
    {
56
        $costs = [];
57
        $gradient = [];
58
        $totalPenalty = 0;
59
60
        foreach ($this->samples as $index => $sample) {
61
            $target = $this->targets[$index];
62
63
            $result = ($this->gradientCb)($theta, $sample, $target);
64
            [$cost, $grad, $penalty] = array_pad($result, 3, 0);
0 ignored issues
show
The variable $cost does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
The variable $grad does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
The variable $penalty does not exist. Did you mean $totalPenalty?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
65
66
            $costs[] = $cost;
67
            $gradient[] = $grad;
68
            $totalPenalty += $penalty;
0 ignored issues
show
The variable $penalty does not exist. Did you mean $totalPenalty?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
69
        }
70
71
        $totalPenalty /= $this->sampleCount;
72
73
        return [$costs, $gradient, $totalPenalty];
74
    }
75
76
    protected function updateWeightsWithUpdates(array $updates, float $penalty): void
77
    {
78
        // Updates all weights at once
79
        for ($i = 0; $i <= $this->dimensions; ++$i) {
80
            if ($i === 0) {
81
                $this->theta[0] -= $this->learningRate * array_sum($updates);
82
            } else {
83
                $col = array_column($this->samples, $i - 1);
84
85
                $error = 0;
86
                foreach ($col as $index => $val) {
87
                    $error += $val * $updates[$index];
88
                }
89
90
                $this->theta[$i] -= $this->learningRate *
91
                    ($error + $penalty * $this->theta[$i]);
92
            }
93
        }
94
    }
95
96
    /**
97
     * Clears the optimizer internal vars after the optimization process.
98
     */
99
    protected function clear(): void
100
    {
101
        $this->sampleCount = null;
102
        parent::clear();
103
    }
104
}
105