diff --git a/src/mlpack/methods/ann/CMakeLists.txt b/src/mlpack/methods/ann/CMakeLists.txt index f6e3c5dab3..8e4118e3ad 100644 --- a/src/mlpack/methods/ann/CMakeLists.txt +++ b/src/mlpack/methods/ann/CMakeLists.txt @@ -18,6 +18,7 @@ add_subdirectory(convolution_rules) add_subdirectory(gan) add_subdirectory(rbm) add_subdirectory(augmented) +add_subdirectory(regularizer) # Add directory name to sources. set(DIR_SRCS) diff --git a/src/mlpack/methods/ann/layer/layer_types.hpp b/src/mlpack/methods/ann/layer/layer_types.hpp index a0a2adbfc2..5ea2f9ab3f 100644 --- a/src/mlpack/methods/ann/layer/layer_types.hpp +++ b/src/mlpack/methods/ann/layer/layer_types.hpp @@ -47,6 +47,9 @@ #include #include +// Regularizers. +#include + // Loss function modules. #include @@ -57,14 +60,22 @@ template class BatchNorm; template class DropConnect; template class Glimpse; template class LayerNorm; -template class Linear; -template class LinearNoBias; template class LSTM; template class GRU; template class FastLSTM; template class VRClassReward; template class Concatenate; +template +class Linear; + +template +class LinearNoBias; + template @@ -190,8 +201,8 @@ using LayerTypes = boost::variant< LayerNorm*, LeakyReLU*, CReLU*, - Linear*, - LinearNoBias*, + Linear*, + LinearNoBias*, LogSoftMax*, Lookup*, LSTM*, diff --git a/src/mlpack/methods/ann/layer/linear.hpp b/src/mlpack/methods/ann/layer/linear.hpp index 362a626e87..74c709adfb 100644 --- a/src/mlpack/methods/ann/layer/linear.hpp +++ b/src/mlpack/methods/ann/layer/linear.hpp @@ -14,6 +14,7 @@ #define MLPACK_METHODS_ANN_LAYER_LINEAR_HPP #include +#include #include "layer_types.hpp" @@ -31,7 +32,8 @@ namespace ann /** Artificial Neural Network. */ { */ template < typename InputDataType = arma::mat, - typename OutputDataType = arma::mat + typename OutputDataType = arma::mat, + typename RegularizerType = NoRegularizer > class Linear { @@ -45,7 +47,9 @@ class Linear * @param inSize The number of input units. * @param outSize The number of output units. */ - Linear(const size_t inSize, const size_t outSize); + Linear(const size_t inSize, + const size_t outSize, + RegularizerType regularizer = RegularizerType()); /* * Reset the layer parameter. @@ -146,6 +150,9 @@ class Linear //! Locally-stored output parameter object. OutputDataType outputParameter; + + //! Locally-stored regularizer object. + RegularizerType regularizer; }; // class Linear } // namespace ann diff --git a/src/mlpack/methods/ann/layer/linear_impl.hpp b/src/mlpack/methods/ann/layer/linear_impl.hpp index 92f58351df..127d789716 100644 --- a/src/mlpack/methods/ann/layer/linear_impl.hpp +++ b/src/mlpack/methods/ann/layer/linear_impl.hpp @@ -19,50 +19,60 @@ namespace mlpack { namespace ann /** Artificial Neural Network. */ { -template -Linear::Linear() +template +Linear::Linear() : + inSize(0), + outSize(0) { // Nothing to do here. } -template -Linear::Linear( +template +Linear::Linear( const size_t inSize, - const size_t outSize) : + const size_t outSize, + RegularizerType regularizer) : inSize(inSize), - outSize(outSize) + outSize(outSize), + regularizer(regularizer) { weights.set_size(outSize * inSize + outSize, 1); } -template -void Linear::Reset() +template +void Linear::Reset() { weight = arma::mat(weights.memptr(), outSize, inSize, false, false); bias = arma::mat(weights.memptr() + weight.n_elem, outSize, 1, false, false); } -template +template template -void Linear::Forward( +void Linear::Forward( const arma::Mat&& input, arma::Mat&& output) { output = weight * input; output.each_col() += bias; } -template +template template -void Linear::Backward( +void Linear::Backward( const arma::Mat&& /* input */, arma::Mat&& gy, arma::Mat&& g) { g = weight.t() * gy; } -template +template template -void Linear::Gradient( +void Linear::Gradient( const arma::Mat&& input, arma::Mat&& error, arma::Mat&& gradient) @@ -71,11 +81,13 @@ void Linear::Gradient( error * input.t()); gradient.submat(weight.n_elem, 0, gradient.n_elem - 1, 0) = arma::sum(error, 1); + regularizer.Evaluate(weights, gradient); } -template +template template -void Linear::serialize( +void Linear::serialize( Archive& ar, const unsigned int /* version */) { ar & BOOST_SERIALIZATION_NVP(inSize); diff --git a/src/mlpack/methods/ann/layer/linear_no_bias.hpp b/src/mlpack/methods/ann/layer/linear_no_bias.hpp index d171e2c2de..ae7c2832dd 100644 --- a/src/mlpack/methods/ann/layer/linear_no_bias.hpp +++ b/src/mlpack/methods/ann/layer/linear_no_bias.hpp @@ -14,6 +14,7 @@ #define MLPACK_METHODS_ANN_LAYER_LINEAR_NO_BIAS_HPP #include +#include #include "layer_types.hpp" @@ -31,7 +32,8 @@ namespace ann /** Artificial Neural Network. */ { */ template < typename InputDataType = arma::mat, - typename OutputDataType = arma::mat + typename OutputDataType = arma::mat, + typename RegularizerType = NoRegularizer > class LinearNoBias { @@ -44,7 +46,9 @@ class LinearNoBias * @param inSize The number of input units. * @param outSize The number of output units. */ - LinearNoBias(const size_t inSize, const size_t outSize); + LinearNoBias(const size_t inSize, + const size_t outSize, + RegularizerType regularizer = RegularizerType()); /* * Reset the layer parameter. @@ -142,6 +146,9 @@ class LinearNoBias //! Locally-stored output parameter object. OutputDataType outputParameter; + + //! Locally-stored regularizer object. + RegularizerType regularizer; }; // class LinearNoBias } // namespace ann diff --git a/src/mlpack/methods/ann/layer/linear_no_bias_impl.hpp b/src/mlpack/methods/ann/layer/linear_no_bias_impl.hpp index 42070c2d30..41e432da17 100644 --- a/src/mlpack/methods/ann/layer/linear_no_bias_impl.hpp +++ b/src/mlpack/methods/ann/layer/linear_no_bias_impl.hpp @@ -19,57 +19,70 @@ namespace mlpack { namespace ann /** Artificial Neural Network. */ { -template -LinearNoBias::LinearNoBias() +template +LinearNoBias::LinearNoBias() : + inSize(0), + outSize(0) { // Nothing to do here. } -template -LinearNoBias::LinearNoBias( - const size_t inSize, const size_t outSize) : +template +LinearNoBias::LinearNoBias( + const size_t inSize, + const size_t outSize, + RegularizerType regularizer) : inSize(inSize), - outSize(outSize) + outSize(outSize), + regularizer(regularizer) { weights.set_size(outSize * inSize, 1); } -template -void LinearNoBias::Reset() +template +void LinearNoBias::Reset() { weight = arma::mat(weights.memptr(), outSize, inSize, false, false); } -template +template template -void LinearNoBias::Forward( +void LinearNoBias::Forward( const arma::Mat&& input, arma::Mat&& output) { output = weight * input; } -template +template template -void LinearNoBias::Backward( +void LinearNoBias::Backward( const arma::Mat&& /* input */, arma::Mat&& gy, arma::Mat&& g) { g = weight.t() * gy; } -template +template template -void LinearNoBias::Gradient( +void LinearNoBias::Gradient( const arma::Mat&& input, arma::Mat&& error, arma::Mat&& gradient) { gradient.submat(0, 0, weight.n_elem - 1, 0) = arma::vectorise( error * input.t()); + regularizer.Evaluate(weights, gradient); } -template +template template -void LinearNoBias::serialize( +void LinearNoBias::serialize( Archive& ar, const unsigned int /* version */) { ar & BOOST_SERIALIZATION_NVP(inSize); diff --git a/src/mlpack/methods/ann/regularizer/CMakeLists.txt b/src/mlpack/methods/ann/regularizer/CMakeLists.txt new file mode 100644 index 0000000000..1a608b9b40 --- /dev/null +++ b/src/mlpack/methods/ann/regularizer/CMakeLists.txt @@ -0,0 +1,18 @@ +# Define the files we need to compile. +# Anything not in this list will not be compiled into mlpack. +set(SOURCES + lregularizer.hpp + lregularizer_impl.hpp + orthogonal_regularizer.hpp + orthogonal_regularizer_impl.hpp + no_regularizer.hpp +) + +# Add directory name to sources. +set(DIR_SRCS) +foreach(file ${SOURCES}) + set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file}) +endforeach() +# Append sources (with directory name) to list of all mlpack sources (used at +# the parent scope). +set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) diff --git a/src/mlpack/methods/ann/regularizer/lregularizer.hpp b/src/mlpack/methods/ann/regularizer/lregularizer.hpp new file mode 100644 index 0000000000..1cca332887 --- /dev/null +++ b/src/mlpack/methods/ann/regularizer/lregularizer.hpp @@ -0,0 +1,75 @@ +/** + * @file lregularizer.hpp + * @author Saksham Bansal + * + * Generalized L-regularizer, allowing both l1 and l2 regularization methods. + * This also gives several convenience typedefs for commonly used L-regularizers. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_METHODS_ANN_LREGULARIZER_HPP +#define MLPACK_METHODS_ANN_LREGULARIZER_HPP + +#include + +namespace mlpack { +namespace ann { + +/** + * The L_p regularizer for arbitrary integer p. + * + * @tparam Power Power of regularizer; i.e. Power = 1 gives the L1-regularization. + */ +template +class LRegularizer +{ + public: + /** + * Create the regularizer object. + * + * @param factor The factor for regularization. + */ + LRegularizer(double factor = 1.0); + + /** + * Calculate the gradient for regularization. + * + * @tparam MatType Type of weight matrix. + * @param weight The weight matrix to be regularized. + * @param gradient The calculated gradient. + */ + template + void Evaluate(const MatType& weight, MatType& gradient); + + //! Serialize the regularizer (nothing to do). + template + void serialize(Archive& ar, const unsigned int /* version */); + + //! The power of the regularizer. + static const int Power = TPower; + + //! The constant for the regularization + double factor; +}; + +// Convenience typedefs. +/** + * The L1 Regularizer. + */ +typedef LRegularizer<1> L1Regularizer; + +/** + * The L2 Regularizer. + */ +typedef LRegularizer<2> L2Regularizer; + +} // namespace ann +} // namespace mlpack + +// Include implementation. +#include "lregularizer_impl.hpp" + +#endif diff --git a/src/mlpack/methods/ann/regularizer/lregularizer_impl.hpp b/src/mlpack/methods/ann/regularizer/lregularizer_impl.hpp new file mode 100644 index 0000000000..469fc1311c --- /dev/null +++ b/src/mlpack/methods/ann/regularizer/lregularizer_impl.hpp @@ -0,0 +1,63 @@ +/** + * @file lregularization_impl.hpp + * @author Saksham Bansal + * + * Implementation of template specializations of LRegularizer class. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_METHODS_ANN_LREGULARIZER_IMPL_HPP +#define MLPACK_METHODS_ANN_LREGULARIZER_IMPL_HPP + +// In case it hasn't been included. +#include "lregularizer.hpp" + +namespace mlpack { +namespace ann { + +template +LRegularizer::LRegularizer(double factor) : + factor(factor) +{ + // Nothing to do here +} + +// Unspecialized implementation. This should almost never be used... +template +template +void LRegularizer::Evaluate(const MatType& weight, MatType& gradient) +{ + gradient += arma::vectorise(arma::pow(weight, Power - 1) * Power * factor); +} + +// L1-Regularizer specializations. +template<> +template +void LRegularizer<1>::Evaluate(const MatType& weight, MatType& gradient) +{ + gradient += arma::vectorise(factor * weight / arma::abs(weight)); +} + +// L2-Regularizer specializations. +template<> +template +void LRegularizer<2>::Evaluate(const MatType& weight, MatType& gradient) +{ + gradient += arma::vectorise(2 * factor * weight); +} + +template +template +void LRegularizer::serialize( + Archive& ar, const unsigned int /* version */) +{ + ar & BOOST_SERIALIZATION_NVP(factor); +} + +} // namespace ann +} // namespace mlpack + +#endif diff --git a/src/mlpack/methods/ann/regularizer/no_regularizer.hpp b/src/mlpack/methods/ann/regularizer/no_regularizer.hpp new file mode 100644 index 0000000000..b480567ab0 --- /dev/null +++ b/src/mlpack/methods/ann/regularizer/no_regularizer.hpp @@ -0,0 +1,52 @@ +/** + * @file no_regularizer.hpp + * @author Saksham Bansal + * + * Definition of the NoRegularizer class. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_METHODS_ANN_NO_REGULARIZER_HPP +#define MLPACK_METHODS_ANN_NO_REGULARIZER_HPP + +#include + +namespace mlpack { +namespace ann { + +/** + * Implementation of the NoRegularizer. This does not add any + * regularization to the weights. + */ +class NoRegularizer +{ + public: + /** + * Create the regularizer object. + */ + NoRegularizer() + { + // Nothing to do here. + }; + + /** + * Calculate the gradient for regularization. + * + * @tparam MatType Type of weight matrix. + * @param weight The weight matrix to be regularized. + * @param gradient The calculated gradient. + */ + template + void Evaluate(const MatType& /* weight */, MatType& /* gradient */) + { + // Nothing to do here. + } +}; + +} // namespace ann +} // namespace mlpack + +#endif diff --git a/src/mlpack/methods/ann/regularizer/orthogonal_regularizer.hpp b/src/mlpack/methods/ann/regularizer/orthogonal_regularizer.hpp new file mode 100644 index 0000000000..9c0d243a72 --- /dev/null +++ b/src/mlpack/methods/ann/regularizer/orthogonal_regularizer.hpp @@ -0,0 +1,72 @@ +/** + * @file orthogonal_regularizer.hpp + * @author Saksham Bansal + * + * Definition of the OrthogonalRegularizer class. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_METHODS_ANN_ORTHOGONAL_REGULARIZER_HPP +#define MLPACK_METHODS_ANN_ORTHOGONAL_REGULARIZER_HPP + +#include + +namespace mlpack { +namespace ann { + +/** + * Implementation of the OrthogonalRegularizer. Orthogonality of weights is a + * desirable property because multiplication by an orthogonal matrix leaves + * the norm of the matrix unchanged. The orthogonal regularization technique + * encourages weights to be orthogonal. + * + * For more information, see the following. + * + * @code + * @inproceedings{WanICML2013, + * title={Neural Photo Editing with Introspective Adversarial Networks}, + * booktitle = {5th International Conference on Learning Representations + * (ICLR - 17)}, + * author = {Andrew Brock and Theodore Lim and J.M. Ritchie and Nick Weston}, + * year = {2017} + * } + * @endcode + */ +class OrthogonalRegularizer +{ + public: + /** + * Create the regularizer object. + * + * @param factor The factor for regularization. + */ + OrthogonalRegularizer(double factor = 1.0); + + /** + * Calculate the gradient for regularization. + * + * @tparam MatType Type of weight matrix. + * @param weight The weight matrix to be regularized. + * @param gradient The calculated gradient. + */ + template + void Evaluate(const MatType& weight, MatType& gradient); + + //! Serialize the regularizer (nothing to do). + template + void serialize(Archive& ar, const unsigned int /* version */); + + //! The constant for the regularization + double factor; +}; + +} // namespace ann +} // namespace mlpack + +// Include implementation. +#include "orthogonal_regularizer_impl.hpp" + +#endif diff --git a/src/mlpack/methods/ann/regularizer/orthogonal_regularizer_impl.hpp b/src/mlpack/methods/ann/regularizer/orthogonal_regularizer_impl.hpp new file mode 100644 index 0000000000..d1f3411e7e --- /dev/null +++ b/src/mlpack/methods/ann/regularizer/orthogonal_regularizer_impl.hpp @@ -0,0 +1,66 @@ +/** + * @file orthogonal_regularizer_impl.hpp + * @author Saksham Bansal + * + * Implementation of OrthogonalRegularizer class. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_METHODS_ANN_ORTHOGONAL_REGULARIZER_IMPL_HPP +#define MLPACK_METHODS_ANN_ORTHOGONAL_REGULARIZER_IMPL_HPP + +// In case it hasn't been included. +#include "orthogonal_regularizer.hpp" + +namespace mlpack { +namespace ann { + +OrthogonalRegularizer::OrthogonalRegularizer(double factor) : + factor(factor) +{ + // Nothing to do here. +} + +template +void OrthogonalRegularizer::Evaluate(const MatType& weight, MatType& gradient) +{ + arma::mat grad = arma::zeros(arma::size(weight)); + + for (size_t i = 0; i < weight.n_rows; i++) + { + for (size_t j = 0; j < weight.n_rows; j++) + { + if (i == j) + { + double s = + arma::as_scalar( + arma::sign((weight.row(i) * weight.row(i).t()) - 1)); + grad.row(i) += 2 * s * weight.row(i); + } + else + { + double s = arma::as_scalar( + arma::sign(weight.row(i) * weight.row(j).t())); + grad.row(i) += s * weight.row(j); + grad.row(j) += s * weight.row(i); + } + } + } + + gradient += arma::vectorise(grad) * factor; +} + +template +void OrthogonalRegularizer::serialize( + Archive& ar, const unsigned int /* version */) +{ + ar & BOOST_SERIALIZATION_NVP(factor); +} + +} // namespace ann +} // namespace mlpack + +#endif diff --git a/src/mlpack/methods/ann/regularizer/regularizer.hpp b/src/mlpack/methods/ann/regularizer/regularizer.hpp new file mode 100644 index 0000000000..c5dd506170 --- /dev/null +++ b/src/mlpack/methods/ann/regularizer/regularizer.hpp @@ -0,0 +1,19 @@ +/** + * @file regularizer.hpp + * @author Saksham Bansal + * + * This includes various regularizers to construct a model. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_METHODS_ANN_REGULARIZER_HPP +#define MLPACK_METHODS_ANN_REGULARIZER_HPP + +#include "lregularizer.hpp" +#include "orthogonal_regularizer.hpp" +#include "no_regularizer.hpp" + +#endif diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index b1d80eed5a..ba866f0a3e 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(mlpack_test aknn_test.cpp ann_dist_test.cpp ann_layer_test.cpp + ann_regularizer_test.cpp ann_test_tools.hpp arma_extend_test.cpp armadillo_svd_test.cpp diff --git a/src/mlpack/tests/ann_regularizer_test.cpp b/src/mlpack/tests/ann_regularizer_test.cpp new file mode 100644 index 0000000000..ba7f3b58ee --- /dev/null +++ b/src/mlpack/tests/ann_regularizer_test.cpp @@ -0,0 +1,117 @@ +/** + * @file ann_regularizer_test.cpp + * @author Saksham Bansal + * + * Tests the ANN regularizer modules. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include + +#include +#include +#include +#include + +#include +#include "ann_test_tools.hpp" +#include "serialization.hpp" + +using namespace mlpack; +using namespace mlpack::ann; + +BOOST_AUTO_TEST_SUITE(ANNRegularizerTest); + +BOOST_AUTO_TEST_CASE(GradientL1RegularizerTest) +{ + // Add function gradient instantiation. + struct GradientFunction + { + GradientFunction() : + factor(0.6), + reg(factor) + { + // Nothing to do here. + } + + double Output(const arma::mat& weight, size_t i, size_t j) + { + return std::abs(weight(i, j)) * factor; + } + + void Gradient(arma::mat& weight, arma::mat& gradient) + { + reg.Evaluate(weight, gradient); + } + + double factor; + L1Regularizer reg; + } function; + + BOOST_REQUIRE_LE(CheckRegularizerGradient(function), 1e-4); +} + +BOOST_AUTO_TEST_CASE(GradientL2RegularizerTest) +{ + // Add function gradient instantiation. + struct GradientFunction + { + GradientFunction() : + factor(0.6), + reg(factor) + { + // Nothing to do here. + } + + double Output(const arma::mat& weight, size_t i, size_t j) + { + return weight(i, j) * weight(i, j) * factor; + } + + void Gradient(arma::mat& weight, arma::mat& gradient) + { + reg.Evaluate(weight, gradient); + } + + double factor; + L2Regularizer reg; + } function; + + BOOST_REQUIRE_LE(CheckRegularizerGradient(function), 1e-4); +} + +BOOST_AUTO_TEST_CASE(GradientOrthogonalRegularizerTest) +{ + // Add function gradient instantiation. + struct GradientFunction + { + GradientFunction() : + factor(0.6), + reg(factor) + { + // Nothing to do here. + } + + double Output(const arma::mat& weight, size_t /* i */, size_t /* j */) + { + arma::mat x = arma::abs(weight * weight.t() - + arma::eye(weight.n_rows, weight.n_cols)) * factor; + return arma::accu(x); + } + + void Gradient(arma::mat& weight, arma::mat& gradient) + { + reg.Evaluate(weight, gradient); + } + + double factor; + OrthogonalRegularizer reg; + } function; + + BOOST_REQUIRE_LE(CheckRegularizerGradient(function), 1e-4); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/mlpack/tests/ann_test_tools.hpp b/src/mlpack/tests/ann_test_tools.hpp index 67f7108cd0..be789befd5 100644 --- a/src/mlpack/tests/ann_test_tools.hpp +++ b/src/mlpack/tests/ann_test_tools.hpp @@ -167,4 +167,39 @@ double CheckGradient(FunctionType& function, const double eps = 1e-7) arma::norm(orgGradient + estGradient); } +// Simple numerical gradient checker for regularizers. +template +double CheckRegularizerGradient(FunctionType& function, const double eps = 1e-7) +{ + // Get gradients for the current parameters. + arma::mat weight = arma::randu(10, 10); + arma::mat orgGradient = arma::zeros(10 * 10, 1); + function.Gradient(weight, orgGradient); + + arma::mat estGradient = arma::zeros(weight.n_rows, weight.n_cols); + + // Compute numeric approximations to gradient. + for (size_t i = 0; i < weight.n_rows; ++i) + { + for (size_t j = 0; j < weight.n_cols; ++j) + { + double tmp = weight(i, j); + + weight(i, j) += eps; + double costPlus = function.Output(weight, i, j); + weight(i, j) -= (2 * eps); + double costMinus = function.Output(weight, i, j); + + // Restore the weight value. + weight(i, j) = tmp; + estGradient(i, j) = (costPlus - costMinus) / (2 * eps); + } + } + + estGradient = arma::vectorise(estGradient); + // Estimate error of gradient. + return arma::norm(orgGradient - estGradient) / + arma::norm(orgGradient + estGradient); +} + #endif