Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
<?php

declare(strict_types=1);

namespace Phpml\Helper\Optimizer;

use Closure;
use Phpml\Exception\InvalidOperationException;

/**
 * Batch version of Gradient Descent to optimize the weights
 * of a classifier given samples, targets and the objective function to minimize
 */
class GD extends StochasticGD
{
    /**
     * Number of samples given
     *
     * @var int|null
     */
    protected $sampleCount;

    public function runOptimization(array $samples, array $targets, Closure $gradientCb): array
    {
        $this->samples = $samples;
        $this->targets = $targets;
        $this->gradientCb = $gradientCb;
        $this->sampleCount = count($this->samples);

        // Batch learning is executed:
        $currIter = 0;
        $this->costValues = [];
        while ($this->maxIterations > $currIter++) {
            $theta = $this->theta;

            // Calculate update terms for each sample
            [$errors, $updates, $totalPenalty] = $this->gradient($theta);

            $this->updateWeightsWithUpdates($updates, $totalPenalty);

< $this->costValues[] = array_sum($errors) / $this->sampleCount;
> $this->costValues[] = array_sum($errors) / (int) $this->sampleCount;
if ($this->earlyStop($theta)) { break; } } $this->clear(); return $this->theta; } /** * Calculates gradient, cost function and penalty term for each sample * then returns them as an array of values */ protected function gradient(array $theta): array { $costs = []; $gradient = []; $totalPenalty = 0; if ($this->gradientCb === null) { throw new InvalidOperationException('Gradient callback is not defined'); } foreach ($this->samples as $index => $sample) { $target = $this->targets[$index]; $result = ($this->gradientCb)($theta, $sample, $target); [$cost, $grad, $penalty] = array_pad($result, 3, 0); $costs[] = $cost; $gradient[] = $grad; $totalPenalty += $penalty; } $totalPenalty /= $this->sampleCount; return [$costs, $gradient, $totalPenalty]; } protected function updateWeightsWithUpdates(array $updates, float $penalty): void { // Updates all weights at once for ($i = 0; $i <= $this->dimensions; ++$i) { if ($i === 0) { $this->theta[0] -= $this->learningRate * array_sum($updates); } else { $col = array_column($this->samples, $i - 1); $error = 0; foreach ($col as $index => $val) { $error += $val * $updates[$index]; } $this->theta[$i] -= $this->learningRate * ($error + $penalty * $this->theta[$i]); } } } /** * Clears the optimizer internal vars after the optimization process. */ protected function clear(): void { $this->sampleCount = null; parent::clear(); } }