From 106c07016fdfb10e6ac2b36516ddc10ebf01c0b7 Mon Sep 17 00:00:00 2001 From: Samuel Akopyan Date: Sun, 25 Jan 2026 16:22:18 +0200 Subject: [PATCH 1/5] ML-394 Converted FeedForward to use NumPower --- src/NeuralNet/FeedForwards/FeedForward.php | 274 ++++++++++++++++++ .../FeedForwards/FeedForwardTest.php | 131 +++++++++ 2 files changed, 405 insertions(+) create mode 100644 src/NeuralNet/FeedForwards/FeedForward.php create mode 100644 tests/NeuralNet/FeedForwards/FeedForwardTest.php diff --git a/src/NeuralNet/FeedForwards/FeedForward.php b/src/NeuralNet/FeedForwards/FeedForward.php new file mode 100644 index 000000000..af430d083 --- /dev/null +++ b/src/NeuralNet/FeedForwards/FeedForward.php @@ -0,0 +1,274 @@ + + */ +class FeedForward +{ + /** + * The input layer to the network. + * + * @var Input + */ + protected Input $input; + + /** + * The hidden layers of the network. + * + * @var list + */ + protected array $hidden = [ + // + ]; + + /** + * The pathing of the backward pass through the hidden layers. + * + * @var list + */ + protected array $backPass = [ + // + ]; + + /** + * The output layer of the network. + * + * @var Output + */ + protected Output $output; + + /** + * The gradient descent optimizer used to train the network. + * + * @var Optimizer + */ + protected Optimizer $optimizer; + + /** + * @param Input $input + * @param Hidden[] $hidden + * @param Output $output + * @param Optimizer $optimizer + */ + public function __construct(Input $input, array $hidden, Output $output, Optimizer $optimizer) + { + $hidden = array_values($hidden); + + $backPass = array_reverse($hidden); + + $this->input = $input; + $this->hidden = $hidden; + $this->output = $output; + $this->optimizer = $optimizer; + $this->backPass = $backPass; + } + + /** + * Return the input layer. + * + * @return Input + */ + public function input() : Input + { + return $this->input; + } + + /** + * Return an array of hidden layers indexed left to right. + * + * @return list + */ + public function hidden() : array + { + return $this->hidden; + } + + /** + * Return the output layer. + * + * @return Output + */ + public function output() : Output + { + return $this->output; + } + + /** + * Return all the layers in the network. + * + * @return Traversable + */ + public function layers() : Traversable + { + yield $this->input; + + yield from $this->hidden; + + yield $this->output; + } + + /** + * Return the number of trainable parameters in the network. + * + * @return int + */ + public function numParams() : int + { + $numParams = 0; + + foreach ($this->layers() as $layer) { + if ($layer instanceof Parametric) { + foreach ($layer->parameters() as $parameter) { + $numParams += $parameter->param()->size(); + } + } + } + + return $numParams; + } + + /** + * Initialize the parameters of the layers and warm the optimizer cache. + */ + public function initialize() : void + { + $fanIn = 1; + + foreach ($this->layers() as $layer) { + $fanIn = $layer->initialize($fanIn); + } + + if ($this->optimizer instanceof Adaptive) { + foreach ($this->layers() as $layer) { + if ($layer instanceof Parametric) { + foreach ($layer->parameters() as $param) { + $this->optimizer->warm($param); + } + } + } + } + } + + /** + * Run an inference pass and return the activations at the output layer. + * + * @param Dataset $dataset + * @return NDArray + */ + public function infer(Dataset $dataset) : NDArray + { + $input = NumPower::transpose(NumPower::array($dataset->samples()), [1, 0]); + + foreach ($this->layers() as $layer) { + $input = $layer->infer($input); + } + + return NumPower::transpose($input, [1, 0]); + } + + /** + * Perform a forward and backward pass of the network in one call. Returns + * the loss from the backward pass. + * + * @param Labeled $dataset + * @return float + */ + public function roundtrip(Labeled $dataset) : float + { + $input = NumPower::transpose(NumPower::array($dataset->samples()), [1, 0]); + + $this->feed($input); + + $loss = $this->backpropagate($dataset->labels()); + + return $loss; + } + + /** + * Feed a batch through the network and return a matrix of activations at the output later. + * + * @param NDArray $input + * @return NDArray + */ + public function feed(NDArray $input) : NDArray + { + foreach ($this->layers() as $layer) { + $input = $layer->forward($input); + } + + return $input; + } + + /** + * Backpropagate the gradient of the cost function and return the loss. + * + * @param list $labels + * @return float + */ + public function backpropagate(array $labels) : float + { + [$gradient, $loss] = $this->output->back($labels, $this->optimizer); + + foreach ($this->backPass as $layer) { + $gradient = $layer->back($gradient, $this->optimizer); + } + + return $loss; + } + + /** + * Export the network architecture as a graph in dot format. + * + * @return Encoding + */ + public function exportGraphviz() : Encoding + { + $dot = 'digraph Tree {' . PHP_EOL; + $dot .= ' node [shape=box, fontname=helvetica];' . PHP_EOL; + + $layerNum = 0; + + foreach ($this->layers() as $layer) { + ++$layerNum; + + $dot .= " N$layerNum [label=\"$layer\",style=\"rounded\"]" . PHP_EOL; + + if ($layerNum > 1) { + $parentId = $layerNum - 1; + + $dot .= " N{$parentId} -> N{$layerNum};" . PHP_EOL; + } + } + + $dot .= '}'; + + return new Encoding($dot); + } +} diff --git a/tests/NeuralNet/FeedForwards/FeedForwardTest.php b/tests/NeuralNet/FeedForwards/FeedForwardTest.php new file mode 100644 index 000000000..ac1c42d53 --- /dev/null +++ b/tests/NeuralNet/FeedForwards/FeedForwardTest.php @@ -0,0 +1,131 @@ +dataset = Labeled::quick([ + [1.0, 2.5], + [0.1, 0.0], + [0.002, -6.0], + ], ['yes', 'no', 'maybe']); + + $this->input = new Placeholder1D(2); + + $this->hidden = [ + new Dense(10), + new Activation(new ReLU()), + new Dense(5), + new Activation(new ReLU()), + new Dense(3), + ]; + + $this->output = new Multiclass(['yes', 'no', 'maybe'], new CrossEntropy()); + + $this->network = new FeedForward($this->input, $this->hidden, $this->output, new Adam(0.001)); + } + + #[Test] + #[TestDox('Builds a feed-forward network instance')] + public function build() : void + { + self::assertInstanceOf(FeedForward::class, $this->network); + } + + #[Test] + #[TestDox('Returns all hidden and output layers')] + public function layers() : void + { + self::assertCount(5, iterator_to_array($this->network->layers())); + } + + #[Test] + #[TestDox('Returns the input layer')] + public function input() : void + { + self::assertInstanceOf(Placeholder1D::class, $this->network->input()); + } + + #[Test] + #[TestDox('Returns the hidden layers')] + public function hidden() : void + { + self::assertCount(5, $this->network->hidden()); + } + + #[Test] + #[TestDox('Returns the output layer')] + public function networkOutput() : void + { + self::assertInstanceOf(Output::class, $this->network->output()); + } + + #[Test] + #[TestDox('Reports the correct number of parameters after initialization')] + public function numParams() : void + { + $this->network->initialize(); + + self::assertEquals(103, $this->network->numParams()); + } + + #[Test] + #[TestDox('Performs a roundtrip pass and returns a loss value')] + public function roundtrip() : void + { + $this->network->initialize(); + + $loss = $this->network->roundtrip($this->dataset); + + self::assertIsFloat($loss); + } +} From e118309f93c81837589ea4eb3eda4fe486fa3009 Mon Sep 17 00:00:00 2001 From: Samuel Akopyan Date: Sun, 25 Jan 2026 16:37:36 +0200 Subject: [PATCH 2/5] ML-394 Converted Network to use NumPower --- src/NeuralNet/Networks/Network.php | 275 +++++++++++++++++++++++ tests/NeuralNet/Networks/NetworkTest.php | 101 +++++++++ 2 files changed, 376 insertions(+) create mode 100644 src/NeuralNet/Networks/Network.php create mode 100644 tests/NeuralNet/Networks/NetworkTest.php diff --git a/src/NeuralNet/Networks/Network.php b/src/NeuralNet/Networks/Network.php new file mode 100644 index 000000000..6554940b3 --- /dev/null +++ b/src/NeuralNet/Networks/Network.php @@ -0,0 +1,275 @@ + + */ +class Network +{ + /** + * The input layer to the network. + * + * @var Input + */ + protected Input $input; + + /** + * The hidden layers of the network. + * + * @var list + */ + protected array $hidden = [ + // + ]; + + /** + * The pathing of the backward pass through the hidden layers. + * + * @var list + */ + protected array $backPass = [ + // + ]; + + /** + * The output layer of the network. + * + * @var Output + */ + protected Output $output; + + /** + * The gradient descent optimizer used to train the network. + * + * @var Optimizer + */ + protected Optimizer $optimizer; + + /** + * @param Input $input + * @param Hidden[] $hidden + * @param Output $output + * @param Optimizer $optimizer + */ + public function __construct(Input $input, array $hidden, Output $output, Optimizer $optimizer) + { + $hidden = array_values($hidden); + + $backPass = array_reverse($hidden); + + $this->input = $input; + $this->hidden = $hidden; + $this->output = $output; + $this->optimizer = $optimizer; + $this->backPass = $backPass; + } + + /** + * Return the input layer. + * + * @return Input + */ + public function input() : Input + { + return $this->input; + } + + /** + * Return an array of hidden layers indexed left to right. + * + * @return list + */ + public function hidden() : array + { + return $this->hidden; + } + + /** + * Return the output layer. + * + * @return Output + */ + public function output() : Output + { + return $this->output; + } + + /** + * Return all the layers in the network. + * + * @return Traversable + */ + public function layers() : Traversable + { + yield $this->input; + + yield from $this->hidden; + + yield $this->output; + } + + /** + * Return the number of trainable parameters in the network. + * + * @return int + */ + public function numParams() : int + { + $numParams = 0; + + foreach ($this->layers() as $layer) { + if ($layer instanceof Parametric) { + foreach ($layer->parameters() as $parameter) { + $numParams += $parameter->param()->size(); + } + } + } + + return $numParams; + } + + /** + * Initialize the parameters of the layers and warm the optimizer cache. + */ + public function initialize() : void + { + $fanIn = 1; + + foreach ($this->layers() as $layer) { + $fanIn = $layer->initialize($fanIn); + } + + if ($this->optimizer instanceof Adaptive) { + foreach ($this->layers() as $layer) { + if ($layer instanceof Parametric) { + foreach ($layer->parameters() as $param) { + $this->optimizer->warm($param); + } + } + } + } + } + + /** + * Run an inference pass and return the activations at the output layer. + * + * @param Dataset $dataset + * @return NDArray + */ + public function infer(Dataset $dataset) : NDArray + { + $input = NumPower::transpose(NumPower::array($dataset->samples()), [1, 0]); + + foreach ($this->layers() as $layer) { + $input = $layer->infer($input); + } + + return NumPower::transpose($input, [1, 0]); + } + + /** + * Perform a forward and backward pass of the network in one call. Returns + * the loss from the backward pass. + * + * @param Labeled $dataset + * @return float + */ + public function roundtrip(Labeled $dataset) : float + { + $input = NumPower::transpose(NumPower::array($dataset->samples()), [1, 0]); + + $this->feed($input); + + $loss = $this->backpropagate($dataset->labels()); + + return $loss; + } + + /** + * Feed a batch through the network and return a matrix of activations at the output later. + * + * @param NDArray $input + * @return NDArray + */ + public function feed(NDArray $input) : NDArray + { + foreach ($this->layers() as $layer) { + $input = $layer->forward($input); + } + + return $input; + } + + /** + * Backpropagate the gradient of the cost function and return the loss. + * + * @param list $labels + * @return float + */ + public function backpropagate(array $labels) : float + { + [$gradient, $loss] = $this->output->back($labels, $this->optimizer); + + foreach ($this->backPass as $layer) { + $gradient = $layer->back($gradient, $this->optimizer); + } + + return $loss; + } + + /** + * Export the network architecture as a graph in dot format. + * + * @return Encoding + */ + public function exportGraphviz() : Encoding + { + $dot = 'digraph Tree {' . PHP_EOL; + $dot .= ' node [shape=box, fontname=helvetica];' . PHP_EOL; + + $layerNum = 0; + + foreach ($this->layers() as $layer) { + ++$layerNum; + + $dot .= " N$layerNum [label=\"$layer\",style=\"rounded\"]" . PHP_EOL; + + if ($layerNum > 1) { + $parentId = $layerNum - 1; + + $dot .= " N{$parentId} -> N{$layerNum};" . PHP_EOL; + } + } + + $dot .= '}'; + + return new Encoding($dot); + } +} diff --git a/tests/NeuralNet/Networks/NetworkTest.php b/tests/NeuralNet/Networks/NetworkTest.php new file mode 100644 index 000000000..0197c225d --- /dev/null +++ b/tests/NeuralNet/Networks/NetworkTest.php @@ -0,0 +1,101 @@ +dataset = Labeled::quick( + samples: [ + [1.0, 2.5], + [0.1, 0.0], + [0.002, -6.0], + ], + labels: ['yes', 'no', 'maybe'] + ); + + $this->input = new Placeholder1D(2); + + $this->hidden = [ + new Dense(neurons: 10), + new Activation(new ReLU()), + new Dense(neurons: 5), + new Activation(new ReLU()), + new Dense(neurons: 3), + ]; + + $this->output = new Multiclass( + classes: ['yes', 'no', 'maybe'], + costFn: new CrossEntropy() + ); + + $this->network = new Network( + input: $this->input, + hidden: $this->hidden, + output: $this->output, + optimizer: new Adam(0.001) + ); + } + + public function testLayers() : void + { + $count = 0; + + foreach ($this->network->layers() as $item) { + ++$count; + } + + self::assertSame(7, $count); + } + + public function testInput() : void + { + self::assertInstanceOf(Placeholder1D::class, $this->network->input()); + } + + public function testHidden() : void + { + self::assertCount(5, $this->network->hidden()); + } + + public function testNumParams() : void + { + $this->network->initialize(); + + self::assertEquals(103, $this->network->numParams()); + } +} From 5dfc35491f8103c7be260f24a831d51196f4f976 Mon Sep 17 00:00:00 2001 From: Samuel Akopyan Date: Sun, 25 Jan 2026 16:55:31 +0200 Subject: [PATCH 3/5] ML-394 Converted Snapshot to use NumPower --- src/NeuralNet/Snapshots/Snapshot.php | 90 ++++++++++++++++++++++ tests/NeuralNet/Snapshots/SnapshotTest.php | 64 +++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/NeuralNet/Snapshots/Snapshot.php create mode 100644 tests/NeuralNet/Snapshots/SnapshotTest.php diff --git a/src/NeuralNet/Snapshots/Snapshot.php b/src/NeuralNet/Snapshots/Snapshot.php new file mode 100644 index 000000000..033224d5c --- /dev/null +++ b/src/NeuralNet/Snapshots/Snapshot.php @@ -0,0 +1,90 @@ + + */ +class Snapshot +{ + /** + * The parametric layers of the network. + * + * @var Parametric[] + */ + protected array $layers; + + /** + * The parameters corresponding to each layer in the network at the time of the snapshot. + * + * @var list + */ + protected array $parameters; + + /** + * Take a snapshot of the network. + * + * @param Network $network + * @return Snapshot + */ + public static function take(Network $network) : self + { + $layers = $parameters = []; + + foreach ($network->layers() as $layer) { + if ($layer instanceof Parametric) { + $params = []; + + foreach ($layer->parameters() as $key => $parameter) { + $params[$key] = clone $parameter; + } + + $layers[] = $layer; + $parameters[] = $params; + } + } + + return new self($layers, $parameters); + } + + /** + * Class constructor. + * + * @param Parametric[] $layers + * @param list $parameters + * @throws InvalidArgumentException + */ + public function __construct(array $layers, array $parameters) + { + if (count($layers) !== count($parameters)) { + throw new InvalidArgumentException('Number of layers and parameter groups must be equal.'); + } + + $this->layers = $layers; + $this->parameters = $parameters; + } + + /** + * Restore the network parameters. + */ + public function restore() : void + { + foreach ($this->layers as $i => $layer) { + $layer->restore($this->parameters[$i]); + } + } +} diff --git a/tests/NeuralNet/Snapshots/SnapshotTest.php b/tests/NeuralNet/Snapshots/SnapshotTest.php new file mode 100644 index 000000000..ecde317e3 --- /dev/null +++ b/tests/NeuralNet/Snapshots/SnapshotTest.php @@ -0,0 +1,64 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Number of layers and parameter groups must be equal.'); + + new Snapshot( + layers: [new Dense(1)], + parameters: [] + ); + } + + public function testTake() : void + { + $network = new Network( + input: new Placeholder1D(1), + hidden: [ + new Dense(10), + new Activation(new ELU()), + new Dense(5), + new Activation(new ELU()), + new Dense(1), + ], + output: new Binary( + classes: ['yes', 'no'], + costFn: new CrossEntropy() + ), + optimizer: new Stochastic() + ); + + $network->initialize(); + + $this->expectNotToPerformAssertions(); + + Snapshot::take($network); + } +} From 0e815dae709cde3a3be96c544b63dac98847e287 Mon Sep 17 00:00:00 2001 From: Samuel Akopyan Date: Sun, 25 Jan 2026 16:58:54 +0200 Subject: [PATCH 4/5] ML-394 Improved FeedForwardTest --- src/NeuralNet/FeedForwards/FeedForward.php | 3 ++- tests/NeuralNet/FeedForwards/FeedForwardTest.php | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/NeuralNet/FeedForwards/FeedForward.php b/src/NeuralNet/FeedForwards/FeedForward.php index af430d083..155a0ec00 100644 --- a/src/NeuralNet/FeedForwards/FeedForward.php +++ b/src/NeuralNet/FeedForwards/FeedForward.php @@ -11,6 +11,7 @@ use Rubix\ML\Encoding; use Rubix\ML\Datasets\Dataset; use Rubix\ML\Datasets\Labeled; +use Rubix\ML\NeuralNet\Networks\Network; use Rubix\ML\NeuralNet\Optimizers\Base\Adaptive; use Rubix\ML\NeuralNet\Optimizers\Base\Optimizer; use Traversable; @@ -30,7 +31,7 @@ * @author Andrew DalPino * @author Samuel Akopyan */ -class FeedForward +class FeedForward extends Network { /** * The input layer to the network. diff --git a/tests/NeuralNet/FeedForwards/FeedForwardTest.php b/tests/NeuralNet/FeedForwards/FeedForwardTest.php index ac1c42d53..84226fc70 100644 --- a/tests/NeuralNet/FeedForwards/FeedForwardTest.php +++ b/tests/NeuralNet/FeedForwards/FeedForwardTest.php @@ -11,6 +11,7 @@ use Rubix\ML\NeuralNet\Layers\Multiclass\Multiclass; use Rubix\ML\NeuralNet\Layers\Placeholder1D\Placeholder1D; use Rubix\ML\NeuralNet\Layers\Base\Contracts\Output; +use Rubix\ML\NeuralNet\Networks\Network; use Rubix\ML\NeuralNet\Optimizers\Adam\Adam; use Rubix\ML\NeuralNet\ActivationFunctions\ReLU\ReLU; use Rubix\ML\NeuralNet\CostFunctions\CrossEntropy\CrossEntropy; @@ -79,6 +80,7 @@ protected function setUp() : void public function build() : void { self::assertInstanceOf(FeedForward::class, $this->network); + self::assertInstanceOf(Network::class, $this->network); } #[Test] From a261d6930bd77ed31c2e7b267c74562bf6e0cf30 Mon Sep 17 00:00:00 2001 From: Samuel Akopyan Date: Sun, 1 Feb 2026 15:45:53 +0200 Subject: [PATCH 5/5] ML-394 Fixed copilot style comments, added prevention division by zero in Swish --- src/NeuralNet/FeedForwards/FeedForward.php | 5 +++-- src/NeuralNet/Layers/Base/Contracts/Layer.php | 2 +- src/NeuralNet/Layers/Swish/Swish.php | 12 +++++++----- tests/Helpers/GraphvizTest.php | 2 +- tests/NeuralNet/Layers/Binary/BinaryTest.php | 10 ++++------ .../Layers/Placeholder1D/Placeholder1DTest.php | 2 +- tests/NeuralNet/Layers/Placeholder1DTest.php | 2 +- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/NeuralNet/FeedForwards/FeedForward.php b/src/NeuralNet/FeedForwards/FeedForward.php index 155a0ec00..aea7fe6ed 100644 --- a/src/NeuralNet/FeedForwards/FeedForward.php +++ b/src/NeuralNet/FeedForwards/FeedForward.php @@ -6,6 +6,7 @@ use NumPower; use Rubix\ML\NeuralNet\Layers\Base\Contracts\Hidden; use Rubix\ML\NeuralNet\Layers\Base\Contracts\Input; +use Rubix\ML\NeuralNet\Layers\Base\Contracts\Layer; use Rubix\ML\NeuralNet\Layers\Base\Contracts\Output; use Rubix\ML\NeuralNet\Layers\Base\Contracts\Parametric; use Rubix\ML\Encoding; @@ -104,7 +105,7 @@ public function input() : Input /** * Return an array of hidden layers indexed left to right. * - * @return list + * @return list */ public function hidden() : array { @@ -124,7 +125,7 @@ public function output() : Output /** * Return all the layers in the network. * - * @return Traversable + * @return Traversable */ public function layers() : Traversable { diff --git a/src/NeuralNet/Layers/Base/Contracts/Layer.php b/src/NeuralNet/Layers/Base/Contracts/Layer.php index 10cf17b6e..dddff074f 100644 --- a/src/NeuralNet/Layers/Base/Contracts/Layer.php +++ b/src/NeuralNet/Layers/Base/Contracts/Layer.php @@ -6,7 +6,7 @@ use Stringable; /** - * Hidden + * Layer * * @category Machine Learning * @package Rubix/ML diff --git a/src/NeuralNet/Layers/Swish/Swish.php b/src/NeuralNet/Layers/Swish/Swish.php index fcb00fa44..575e11df8 100644 --- a/src/NeuralNet/Layers/Swish/Swish.php +++ b/src/NeuralNet/Layers/Swish/Swish.php @@ -15,6 +15,8 @@ use Rubix\ML\NeuralNet\Parameters\Parameter; use Generator; +use const Rubix\ML\EPSILON; + /** * Swish * @@ -266,6 +268,7 @@ protected function activate(NDArray $input) : NDArray /** * Calculate the derivative of the activation function at a given output. + * Formulation: derivative = (output / input) * (1 - output) + output * * @param NDArray $input * @param NDArray $output @@ -278,12 +281,11 @@ protected function differentiate(NDArray $input, NDArray $output) : NDArray throw new RuntimeException('Layer has not been initialized.'); } - // Original formulation: - // derivative = (output / input) * (1 - output) + output - // Implemented using NumPower operations to avoid explicit ones matrix. - $term1 = NumPower::divide($output, $input); - $oneMinusOutput = NumPower::subtract(1.0, $output); + // Prevent division by zero if the input contains zero values + $denominator = NumPower::add($input, EPSILON); + $term1 = NumPower::divide($output, $denominator); + $oneMinusOutput = NumPower::subtract(1.0, $output); $product = NumPower::multiply($term1, $oneMinusOutput); return NumPower::add($product, $output); diff --git a/tests/Helpers/GraphvizTest.php b/tests/Helpers/GraphvizTest.php index 3bef96a06..6493d97de 100644 --- a/tests/Helpers/GraphvizTest.php +++ b/tests/Helpers/GraphvizTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\TestCase; #[Group('Helpers')] -#[CoversClass(GraphvizTest::class)] +#[CoversClass(Graphviz::class)] class GraphvizTest extends TestCase { #[Test] diff --git a/tests/NeuralNet/Layers/Binary/BinaryTest.php b/tests/NeuralNet/Layers/Binary/BinaryTest.php index 645d7c86b..ad129bf97 100644 --- a/tests/NeuralNet/Layers/Binary/BinaryTest.php +++ b/tests/NeuralNet/Layers/Binary/BinaryTest.php @@ -1,6 +1,6 @@ }> + * @return array}> */ public static function badClassesProvider() : array { @@ -155,7 +155,7 @@ public function testBack(array $expectedGradient) : void } #[Test] - #[TestDox('Computes gradient directly given input, output, expected, and batch size')] + #[TestDox('Computes gradient directly given input, output and expected')] #[DataProvider('backProvider')] public function testGradient(array $expectedGradient) : void { @@ -171,9 +171,7 @@ public function testGradient(array $expectedGradient) : void } $expected = NumPower::array([$expected]); - $batchSize = count($this->labels); - - $gradient = $this->layer->gradient($input, $output, $expected, $batchSize); + $gradient = $this->layer->gradient($input, $output, $expected); self::assertInstanceOf(NDArray::class, $gradient); self::assertEqualsWithDelta($expectedGradient, $gradient->toArray(), 1e-7); diff --git a/tests/NeuralNet/Layers/Placeholder1D/Placeholder1DTest.php b/tests/NeuralNet/Layers/Placeholder1D/Placeholder1DTest.php index 7aa3168c8..81dbc91cf 100644 --- a/tests/NeuralNet/Layers/Placeholder1D/Placeholder1DTest.php +++ b/tests/NeuralNet/Layers/Placeholder1D/Placeholder1DTest.php @@ -1,6 +1,6 @@