diff --git a/src/NeuralNet/Layers/BatchNorm.php b/src/NeuralNet/Layers/BatchNorm.php index 421ec9607..7e7d0e15b 100644 --- a/src/NeuralNet/Layers/BatchNorm.php +++ b/src/NeuralNet/Layers/BatchNorm.php @@ -181,7 +181,7 @@ public function forward(Matrix $input) : Matrix } $mean = $input->mean(); - $variance = $input->variance($mean)->clipLower(EPSILON); + $variance = $input->subtractColumnVector($mean)->square()->mean()->clipLower(EPSILON); $stdInv = $variance->sqrt()->reciprocal(); $xHat = $stdInv->multiply($input->subtract($mean)); @@ -287,10 +287,10 @@ public function gradient(Matrix $dOut, ColumnVector $gamma, ColumnVector $stdInv $dXHatSigma = $dXHat->sum(); - return $dXHat->multiply($dOut->m()) + return $dXHat->multiply($dOut->n()) ->subtract($dXHatSigma) ->subtract($xHat->multiply($xHatSigma)) - ->multiply($stdInv->divide($dOut->m())); + ->multiply($stdInv->divide($dOut->n())); } /** diff --git a/tests/NeuralNet/Layers/BatchNormTest.php b/tests/NeuralNet/Layers/BatchNormTest.php index 211b0bdd6..fef6b7524 100644 --- a/tests/NeuralNet/Layers/BatchNormTest.php +++ b/tests/NeuralNet/Layers/BatchNormTest.php @@ -122,4 +122,65 @@ public function initializeForwardBackInfer() : void $this->assertInstanceOf(Matrix::class, $infer); $this->assertEqualsWithDelta($expected, $infer->asArray(), 1e-8); } + + /** + * @test + */ + public function normalizesOverBatchSize() : void + { + $fanIn = 3; + + $input = Matrix::quick([ + [1.0, 2.5, -0.1, 0.5], + [0.1, 0.0, 3.0, -1.0], + [0.002, -6.0, -0.5, 2.0], + ]); + + $prevGrad = new Deferred(function () { + return Matrix::quick([ + [0.25, 0.7, 0.1, 0.3], + [0.50, 0.2, 0.01, -0.4], + [0.25, 0.1, 0.89, 0.6], + ]); + }); + + $optimizer = new Stochastic(0.001); + + $layer = new BatchNorm(0.9, new Constant(0.), new Constant(1.)); + + $layer->initialize($fanIn); + + $expected = [ + [0.025967457200229, 1.584014889214, -1.1166006596098, -0.49338168680435], + [-0.28480067232747, -0.35181259522806, 1.6585450917894, -1.0219318242339], + [0.3797862163235, -1.643717441354, 0.21054282476168, 1.0533884002688], + ]; + + $forward = $layer->forward($input); + + $this->assertInstanceOf(Matrix::class, $forward); + $this->assertEqualsWithDelta($expected, $forward->asArray(), 1e-8); + + $expected = [ + [-0.096655673462753, 0.024584160424222, 0.0014008068617791, 0.070670706176752], + [0.29326885034768, 0.094619781903042, -0.10430387932137, -0.28358475292935], + [-0.094806324008858, -0.017466016738221, 0.13166046771019, -0.019388126963108], + ]; + + $gradient = $layer->back($prevGrad, $optimizer)->compute(); + + $this->assertInstanceOf(Matrix::class, $gradient); + $this->assertEqualsWithDelta($expected, $gradient->asArray(), 1e-8); + + $expected = [ + [0.024595238724167, 1.5813095621742, -1.1169952651392, -0.49430953575917], + [-0.28505012503587, -0.35204780151489, 1.6578824928559, -1.0220245663052], + [0.37766138009295, -1.6443246681253, 0.20854491954554, 1.0507583684868], + ]; + + $infer = $layer->infer($input); + + $this->assertInstanceOf(Matrix::class, $infer); + $this->assertEqualsWithDelta($expected, $infer->asArray(), 1e-8); + } }