Merge pull request #1441 from akhandait/reconstruction_loss
Add ReconstructionLoss
This commit is contained in:
@@ -134,6 +134,15 @@ class FFN
|
||||
*/
|
||||
void Predict(arma::mat predictors, arma::mat& results);
|
||||
|
||||
/**
|
||||
* Evaluate the feedforward network with the given ppredictors and responses.
|
||||
* This functions is usually used to monitor progress while training.
|
||||
*
|
||||
* @param predictors Input variables.
|
||||
* @param responses Target outputs for input variables.
|
||||
*/
|
||||
double Evaluate(arma::mat predictors, arma::mat responses);
|
||||
|
||||
/**
|
||||
* Evaluate the feedforward network with the given parameters. This function
|
||||
* is usually called by the optimizer to train the model.
|
||||
|
||||
@@ -203,6 +203,33 @@ void FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Predict(
|
||||
}
|
||||
}
|
||||
|
||||
template<typename OutputLayerType, typename InitializationRuleType,
|
||||
typename... CustomLayers>
|
||||
double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Evaluate(
|
||||
arma::mat predictors, arma::mat responses)
|
||||
{
|
||||
if (parameter.is_empty())
|
||||
ResetParameters();
|
||||
|
||||
if (!deterministic)
|
||||
{
|
||||
deterministic = true;
|
||||
ResetDeterministic();
|
||||
}
|
||||
|
||||
Forward(std::move(predictors));
|
||||
|
||||
double res = outputLayer.Forward(std::move(boost::apply_visitor(
|
||||
outputParameterVisitor, network.back())), std::move(responses));
|
||||
|
||||
for (size_t i = 0; i < network.size(); ++i)
|
||||
{
|
||||
res += boost::apply_visitor(lossVisitor, network[i]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename OutputLayerType, typename InitializationRuleType,
|
||||
typename... CustomLayers>
|
||||
double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Evaluate(
|
||||
|
||||
@@ -34,6 +34,9 @@ namespace ann /** Artificial Neural Network. */ {
|
||||
* feed-forward fully connected network container which plugs various layers
|
||||
* together.
|
||||
*
|
||||
* Note: If this class is used as the first layer of a network, it should be
|
||||
* preceded by IdentityLayer<>.
|
||||
*
|
||||
* @tparam InputDataType Type of the input data (arma::colvec, arma::mat,
|
||||
* arma::sp_mat or arma::cube).
|
||||
* @tparam OutputDataType Type of the output data (arma::colvec, arma::mat,
|
||||
|
||||
@@ -138,15 +138,20 @@ void Sequential<InputDataType, OutputDataType, CustomLayers...>::Gradient(
|
||||
arma::Mat<eT>&& error,
|
||||
arma::Mat<eT>&& /* gradient */)
|
||||
{
|
||||
boost::apply_visitor(GradientVisitor(std::move(input), std::move(error)),
|
||||
network.front());
|
||||
boost::apply_visitor(GradientVisitor(std::move(boost::apply_visitor(
|
||||
outputParameterVisitor, network[network.size() - 2])), std::move(error)),
|
||||
network.back());
|
||||
|
||||
for (size_t i = 1; i < network.size() - 1; ++i)
|
||||
for (size_t i = 2; i < network.size(); ++i)
|
||||
{
|
||||
boost::apply_visitor(GradientVisitor(std::move(boost::apply_visitor(
|
||||
outputParameterVisitor, network[i - 1])), std::move(
|
||||
boost::apply_visitor(deltaVisitor, network[i + 1]))), network[i]);
|
||||
outputParameterVisitor, network[network.size() - i - 1])), std::move(
|
||||
boost::apply_visitor(deltaVisitor, network[network.size() - i + 1]))),
|
||||
network[network.size() - i]);
|
||||
}
|
||||
|
||||
boost::apply_visitor(GradientVisitor(std::move(input), std::move(
|
||||
boost::apply_visitor(deltaVisitor, network[1]))), network.front());
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
|
||||
@@ -11,6 +11,8 @@ set(SOURCES
|
||||
mean_squared_error_impl.hpp
|
||||
negative_log_likelihood.hpp
|
||||
negative_log_likelihood_impl.hpp
|
||||
reconstruction_loss.hpp
|
||||
reconstruction_loss_impl.hpp
|
||||
sigmoid_cross_entropy_error.hpp
|
||||
sigmoid_cross_entropy_error_impl.hpp
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@ class CrossEntropyError
|
||||
* Computes the cross-entropy function.
|
||||
*
|
||||
* @param input Input data used for evaluating the specified function.
|
||||
* @param output Resulting output activation.
|
||||
* @param target The target vector.
|
||||
*/
|
||||
template<typename InputType, typename TargetType>
|
||||
double Forward(const InputType&& input, const TargetType&& target);
|
||||
|
||||
@@ -42,7 +42,7 @@ class EarthMoverDistance
|
||||
* Ordinary feed forward pass of a neural network.
|
||||
*
|
||||
* @param input Input data used for evaluating the specified function.
|
||||
* @param output Resulting output activation.
|
||||
* @param target The target vector.
|
||||
*/
|
||||
template<typename InputType, typename TargetType>
|
||||
double Forward(const InputType&& input, const TargetType&& target);
|
||||
|
||||
@@ -43,7 +43,7 @@ class MeanSquaredError
|
||||
* Computes the mean squared error function.
|
||||
*
|
||||
* @param input Input data used for evaluating the specified function.
|
||||
* @param output Resulting output activation.
|
||||
* @param target The target vector.
|
||||
*/
|
||||
template<typename InputType, typename TargetType>
|
||||
double Forward(const InputType&& input, const TargetType&& target);
|
||||
|
||||
@@ -44,7 +44,8 @@ class NegativeLogLikelihood
|
||||
* Computes the Negative log likelihood.
|
||||
*
|
||||
* @param input Input data used for evaluating the specified function.
|
||||
* @param output Resulting output activation.
|
||||
* @param target The target vector, that contains the class index in the range
|
||||
* between 1 and the number of classes.
|
||||
*/
|
||||
template<typename InputType, typename TargetType>
|
||||
double Forward(const InputType&& input, TargetType&& target);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @file reconstruction_loss.hpp
|
||||
* @author Atharva Khandait
|
||||
*
|
||||
* Definition of the reconstruction loss performance function.
|
||||
*
|
||||
* 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_LOSS_FUNCTION_RECONSTRUCTION_LOSS_HPP
|
||||
#define MLPACK_METHODS_ANN_LOSS_FUNCTION_RECONSTRUCTION_LOSS_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
#include <mlpack/methods/ann/dists/bernoulli_distribution.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
/**
|
||||
* The reconstruction loss performance function measures the network's
|
||||
* performance equal to the negative log probability of the target with
|
||||
* the input distribution.
|
||||
*
|
||||
* @tparam InputDataType Type of the input data (arma::colvec, arma::mat,
|
||||
* arma::sp_mat or arma::cube).
|
||||
* @tparam OutputDataType Type of the output data (arma::colvec, arma::mat,
|
||||
* arma::sp_mat or arma::cube).
|
||||
* @tparam DistType The type of distribution parametrized by the input.
|
||||
*/
|
||||
template <
|
||||
typename InputDataType = arma::mat,
|
||||
typename OutputDataType = arma::mat,
|
||||
typename DistType = BernoulliDistribution<InputDataType>
|
||||
>
|
||||
class ReconstructionLoss
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create the ReconstructionLoss object.
|
||||
*/
|
||||
ReconstructionLoss();
|
||||
|
||||
/*
|
||||
* Computes the reconstruction loss.
|
||||
*
|
||||
* @param input Input data used for evaluating the specified function.
|
||||
* @param target The target matrix.
|
||||
*/
|
||||
template<typename InputType, typename TargetType>
|
||||
double Forward(const InputType&& input, const TargetType&& target);
|
||||
|
||||
/**
|
||||
* Ordinary feed backward pass of a neural network.
|
||||
*
|
||||
* @param input The propagated input activation.
|
||||
* @param target The target matrix.
|
||||
* @param output The calculated error.
|
||||
*/
|
||||
template<typename InputType, typename TargetType, typename OutputType>
|
||||
void Backward(const InputType&& input,
|
||||
const TargetType&& target,
|
||||
OutputType&& output);
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
OutputDataType& OutputParameter() { return outputParameter; }
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int /* version */);
|
||||
|
||||
private:
|
||||
//! Locally-stored distribution object.
|
||||
DistType dist;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class ReconstructionLoss
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
// Include implementation.
|
||||
#include "reconstruction_loss_impl.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @file reconstruction_loss_impl.hpp
|
||||
* @author Atharva Khandait
|
||||
*
|
||||
* Implementation of the reconstruction loss performance function.
|
||||
*
|
||||
* 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_LOSS_FUNCTION_RECONSTRUCTION_LOSS_IMPL_HPP
|
||||
#define MLPACK_METHODS_ANN_LOSS_FUNCTION_RECONSTRUCTION_LOSS_IMPL_HPP
|
||||
|
||||
// In case it hasn't yet been included.
|
||||
#include "reconstruction_loss.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
template<typename InputDataType, typename OutputDataType, typename DistType>
|
||||
ReconstructionLoss<
|
||||
InputDataType,
|
||||
OutputDataType,
|
||||
DistType
|
||||
>::ReconstructionLoss()
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType, typename DistType>
|
||||
template<typename InputType, typename TargetType>
|
||||
double ReconstructionLoss<InputDataType, OutputDataType, DistType>::Forward(
|
||||
const InputType&& input, const TargetType&& target)
|
||||
{
|
||||
dist = DistType(std::move(input));
|
||||
return -dist.LogProbability(std::move(target));
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType, typename DistType>
|
||||
template<typename InputType, typename TargetType, typename OutputType>
|
||||
void ReconstructionLoss<InputDataType, OutputDataType, DistType>::Backward(
|
||||
const InputType&& /* input */,
|
||||
const TargetType&& target,
|
||||
OutputType&& output)
|
||||
{
|
||||
dist.LogProbBackward(std::move(target), std::move(output));
|
||||
output *= -1;
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType, typename DistType>
|
||||
template<typename Archive>
|
||||
void ReconstructionLoss<InputDataType, OutputDataType, DistType>::serialize(
|
||||
Archive& /* ar */,
|
||||
const unsigned int /* version */)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -61,7 +61,7 @@ class SigmoidCrossEntropyError
|
||||
* Computes the Sigmoid CrossEntropy Error functions.
|
||||
*
|
||||
* @param input Input data used for evaluating the specified function.
|
||||
* @param output Resulting output activation.
|
||||
* @param target The target vector.
|
||||
*/
|
||||
template<typename InputType, typename TargetType>
|
||||
inline double Forward(const InputType&& input,
|
||||
|
||||
@@ -9,6 +9,7 @@ add_executable(mlpack_test
|
||||
aknn_test.cpp
|
||||
ann_dist_test.cpp
|
||||
ann_layer_test.cpp
|
||||
ann_test_tools.hpp
|
||||
arma_extend_test.cpp
|
||||
armadillo_svd_test.cpp
|
||||
async_learning_test.cpp
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#include <mlpack/methods/ann/layer/layer.hpp>
|
||||
#include <mlpack/methods/ann/layer/layer_types.hpp>
|
||||
#include <mlpack/methods/ann/loss_functions/mean_squared_error.hpp>
|
||||
#include <mlpack/methods/ann/loss_functions/sigmoid_cross_entropy_error.hpp>
|
||||
#include <mlpack/methods/ann/init_rules/random_init.hpp>
|
||||
#include <mlpack/methods/ann/init_rules/const_init.hpp>
|
||||
#include <mlpack/methods/ann/init_rules/nguyen_widrow_init.hpp>
|
||||
@@ -24,162 +22,13 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "test_tools.hpp"
|
||||
#include "ann_test_tools.hpp"
|
||||
|
||||
using namespace mlpack;
|
||||
using namespace mlpack::ann;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(ANNLayerTest);
|
||||
|
||||
// Helper function which calls the Reset function of the given module.
|
||||
template<class T>
|
||||
void ResetFunction(
|
||||
T& layer,
|
||||
typename std::enable_if<HasResetCheck<T, void(T::*)()>::value>::type* = 0)
|
||||
{
|
||||
layer.Reset();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ResetFunction(
|
||||
T& /* layer */,
|
||||
typename std::enable_if<!HasResetCheck<T, void(T::*)()>::value>::type* = 0)
|
||||
{
|
||||
/* Nothing to do here */
|
||||
}
|
||||
|
||||
// Approximate Jacobian and supposedly-true Jacobian, then compare them
|
||||
// similarly to before.
|
||||
template<typename ModuleType>
|
||||
double JacobianTest(ModuleType& module,
|
||||
arma::mat& input,
|
||||
const double minValue = -2,
|
||||
const double maxValue = -1,
|
||||
const double perturbation = 1e-6)
|
||||
{
|
||||
arma::mat output, outputA, outputB, jacobianA, jacobianB;
|
||||
|
||||
// Initialize the input matrix.
|
||||
RandomInitialization init(minValue, maxValue);
|
||||
init.Initialize(input, input.n_rows, input.n_cols);
|
||||
|
||||
// Initialize the module parameters.
|
||||
ResetFunction(module);
|
||||
|
||||
// Initialize the jacobian matrix.
|
||||
module.Forward(std::move(input), std::move(output));
|
||||
jacobianA = arma::zeros(input.n_elem, output.n_elem);
|
||||
|
||||
// Share the input paramter matrix.
|
||||
arma::mat sin = arma::mat(input.memptr(), input.n_rows, input.n_cols,
|
||||
false, false);
|
||||
|
||||
for (size_t i = 0; i < input.n_elem; ++i)
|
||||
{
|
||||
double original = sin(i);
|
||||
sin(i) = original - perturbation;
|
||||
module.Forward(std::move(input), std::move(outputA));
|
||||
sin(i) = original + perturbation;
|
||||
module.Forward(std::move(input), std::move(outputB));
|
||||
sin(i) = original;
|
||||
|
||||
outputB -= outputA;
|
||||
outputB /= 2 * perturbation;
|
||||
jacobianA.row(i) = outputB.t();
|
||||
}
|
||||
|
||||
// Initialize the derivative parameter.
|
||||
arma::mat deriv = arma::zeros(output.n_rows, output.n_cols);
|
||||
|
||||
// Share the derivative parameter.
|
||||
arma::mat derivTemp = arma::mat(deriv.memptr(), deriv.n_rows, deriv.n_cols,
|
||||
false, false);
|
||||
|
||||
// Initialize the jacobian matrix.
|
||||
jacobianB = arma::zeros(input.n_elem, output.n_elem);
|
||||
|
||||
for (size_t i = 0; i < derivTemp.n_elem; ++i)
|
||||
{
|
||||
deriv.zeros();
|
||||
derivTemp(i) = 1;
|
||||
|
||||
arma::mat delta;
|
||||
module.Backward(std::move(input), std::move(deriv), std::move(delta));
|
||||
|
||||
jacobianB.col(i) = delta;
|
||||
}
|
||||
|
||||
return arma::max(arma::max(arma::abs(jacobianA - jacobianB)));
|
||||
}
|
||||
|
||||
// Approximate Jacobian and supposedly-true Jacobian, then compare them
|
||||
// similarly to before.
|
||||
template<typename ModuleType>
|
||||
double JacobianPerformanceTest(ModuleType& module,
|
||||
arma::mat& input,
|
||||
arma::mat& target,
|
||||
const double eps = 1e-6)
|
||||
{
|
||||
module.Forward(std::move(input), std::move(target));
|
||||
|
||||
arma::mat delta;
|
||||
module.Backward(std::move(input), std::move(target), std::move(delta));
|
||||
|
||||
arma::mat centralDifference = arma::zeros(delta.n_rows, delta.n_cols);
|
||||
arma::mat inputTemp = arma::mat(input.memptr(), input.n_rows, input.n_cols,
|
||||
false, false);
|
||||
|
||||
arma::mat centralDifferenceTemp = arma::mat(centralDifference.memptr(),
|
||||
centralDifference.n_rows, centralDifference.n_cols, false, false);
|
||||
|
||||
for (size_t i = 0; i < input.n_elem; ++i)
|
||||
{
|
||||
inputTemp(i) = inputTemp(i) + eps;
|
||||
double outputA = module.Forward(std::move(input), std::move(target));
|
||||
inputTemp(i) = inputTemp(i) - (2 * eps);
|
||||
double outputB = module.Forward(std::move(input), std::move(target));
|
||||
|
||||
centralDifferenceTemp(i) = (outputA - outputB) / (2 * eps);
|
||||
inputTemp(i) = inputTemp(i) + eps;
|
||||
}
|
||||
|
||||
return arma::max(arma::max(arma::abs(centralDifference - delta)));
|
||||
}
|
||||
|
||||
// Simple numerical gradient checker.
|
||||
template<class FunctionType>
|
||||
double CheckGradient(FunctionType& function, const double eps = 1e-7)
|
||||
{
|
||||
// Get gradients for the current parameters.
|
||||
arma::mat orgGradient, gradient, estGradient;
|
||||
function.Gradient(orgGradient);
|
||||
|
||||
estGradient = arma::zeros(orgGradient.n_rows, orgGradient.n_cols);
|
||||
|
||||
// Compute numeric approximations to gradient.
|
||||
for (size_t i = 0; i < orgGradient.n_elem; ++i)
|
||||
{
|
||||
double tmp = function.Parameters()(i);
|
||||
|
||||
// Perturb parameter with a positive constant and get costs.
|
||||
function.Parameters()(i) += eps;
|
||||
double costPlus = function.Gradient(gradient);
|
||||
|
||||
// Perturb parameter with a negative constant and get costs.
|
||||
function.Parameters()(i) -= (2 * eps);
|
||||
double costMinus = function.Gradient(gradient);
|
||||
|
||||
// Restore the parameter value.
|
||||
function.Parameters()(i) = tmp;
|
||||
|
||||
// Compute numerical gradients using the costs calculated above.
|
||||
estGradient(i) = (costPlus - costMinus) / (2 * eps);
|
||||
}
|
||||
|
||||
// Estimate error of gradient.
|
||||
return arma::norm(orgGradient - estGradient) /
|
||||
arma::norm(orgGradient + estGradient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple add module test.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* @file ann_test_tools.hpp
|
||||
* @author Marcus Edel
|
||||
*
|
||||
* This file includes some useful functions for ann tests.
|
||||
*
|
||||
* 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_TESTS_ANN_TEST_TOOLS_HPP
|
||||
#define MLPACK_TESTS_ANN_TEST_TOOLS_HPP
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
|
||||
using namespace mlpack;
|
||||
using namespace mlpack::ann;
|
||||
|
||||
// Helper function which calls the Reset function of the given module.
|
||||
template<class T>
|
||||
void ResetFunction(
|
||||
T& layer,
|
||||
typename std::enable_if<HasResetCheck<T, void(T::*)()>::value>::type* = 0)
|
||||
{
|
||||
layer.Reset();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ResetFunction(
|
||||
T& /* layer */,
|
||||
typename std::enable_if<!HasResetCheck<T, void(T::*)()>::value>::type* = 0)
|
||||
{
|
||||
/* Nothing to do here */
|
||||
}
|
||||
|
||||
// Approximate Jacobian and supposedly-true Jacobian, then compare them
|
||||
// similarly to before.
|
||||
template<typename ModuleType>
|
||||
double JacobianTest(ModuleType& module,
|
||||
arma::mat& input,
|
||||
const double minValue = -2,
|
||||
const double maxValue = -1,
|
||||
const double perturbation = 1e-6)
|
||||
{
|
||||
arma::mat output, outputA, outputB, jacobianA, jacobianB;
|
||||
|
||||
// Initialize the input matrix.
|
||||
RandomInitialization init(minValue, maxValue);
|
||||
init.Initialize(input, input.n_rows, input.n_cols);
|
||||
|
||||
// Initialize the module parameters.
|
||||
ResetFunction(module);
|
||||
|
||||
// Initialize the jacobian matrix.
|
||||
module.Forward(std::move(input), std::move(output));
|
||||
jacobianA = arma::zeros(input.n_elem, output.n_elem);
|
||||
|
||||
// Share the input paramter matrix.
|
||||
arma::mat sin = arma::mat(input.memptr(), input.n_rows, input.n_cols,
|
||||
false, false);
|
||||
|
||||
for (size_t i = 0; i < input.n_elem; ++i)
|
||||
{
|
||||
double original = sin(i);
|
||||
sin(i) = original - perturbation;
|
||||
module.Forward(std::move(input), std::move(outputA));
|
||||
sin(i) = original + perturbation;
|
||||
module.Forward(std::move(input), std::move(outputB));
|
||||
sin(i) = original;
|
||||
|
||||
outputB -= outputA;
|
||||
outputB /= 2 * perturbation;
|
||||
jacobianA.row(i) = outputB.t();
|
||||
}
|
||||
|
||||
// Initialize the derivative parameter.
|
||||
arma::mat deriv = arma::zeros(output.n_rows, output.n_cols);
|
||||
|
||||
// Share the derivative parameter.
|
||||
arma::mat derivTemp = arma::mat(deriv.memptr(), deriv.n_rows, deriv.n_cols,
|
||||
false, false);
|
||||
|
||||
// Initialize the jacobian matrix.
|
||||
jacobianB = arma::zeros(input.n_elem, output.n_elem);
|
||||
|
||||
for (size_t i = 0; i < derivTemp.n_elem; ++i)
|
||||
{
|
||||
deriv.zeros();
|
||||
derivTemp(i) = 1;
|
||||
|
||||
arma::mat delta;
|
||||
module.Backward(std::move(input), std::move(deriv), std::move(delta));
|
||||
|
||||
jacobianB.col(i) = delta;
|
||||
}
|
||||
|
||||
return arma::max(arma::max(arma::abs(jacobianA - jacobianB)));
|
||||
}
|
||||
|
||||
// Approximate Jacobian and supposedly-true Jacobian, then compare them
|
||||
// similarly to before.
|
||||
template<typename ModuleType>
|
||||
double JacobianPerformanceTest(ModuleType& module,
|
||||
arma::mat& input,
|
||||
arma::mat& target,
|
||||
const double eps = 1e-6)
|
||||
{
|
||||
module.Forward(std::move(input), std::move(target));
|
||||
|
||||
arma::mat delta;
|
||||
module.Backward(std::move(input), std::move(target), std::move(delta));
|
||||
|
||||
arma::mat centralDifference = arma::zeros(delta.n_rows, delta.n_cols);
|
||||
arma::mat inputTemp = arma::mat(input.memptr(), input.n_rows, input.n_cols,
|
||||
false, false);
|
||||
|
||||
arma::mat centralDifferenceTemp = arma::mat(centralDifference.memptr(),
|
||||
centralDifference.n_rows, centralDifference.n_cols, false, false);
|
||||
|
||||
for (size_t i = 0; i < input.n_elem; ++i)
|
||||
{
|
||||
inputTemp(i) = inputTemp(i) + eps;
|
||||
double outputA = module.Forward(std::move(input), std::move(target));
|
||||
inputTemp(i) = inputTemp(i) - (2 * eps);
|
||||
double outputB = module.Forward(std::move(input), std::move(target));
|
||||
|
||||
centralDifferenceTemp(i) = (outputA - outputB) / (2 * eps);
|
||||
inputTemp(i) = inputTemp(i) + eps;
|
||||
}
|
||||
|
||||
return arma::max(arma::max(arma::abs(centralDifference - delta)));
|
||||
}
|
||||
|
||||
// Simple numerical gradient checker.
|
||||
template<class FunctionType>
|
||||
double CheckGradient(FunctionType& function, const double eps = 1e-7)
|
||||
{
|
||||
// Get gradients for the current parameters.
|
||||
arma::mat orgGradient, gradient, estGradient;
|
||||
function.Gradient(orgGradient);
|
||||
|
||||
estGradient = arma::zeros(orgGradient.n_rows, orgGradient.n_cols);
|
||||
|
||||
// Compute numeric approximations to gradient.
|
||||
for (size_t i = 0; i < orgGradient.n_elem; ++i)
|
||||
{
|
||||
double tmp = function.Parameters()(i);
|
||||
|
||||
// Perturb parameter with a positive constant and get costs.
|
||||
function.Parameters()(i) += eps;
|
||||
double costPlus = function.Gradient(gradient);
|
||||
|
||||
// Perturb parameter with a negative constant and get costs.
|
||||
function.Parameters()(i) -= (2 * eps);
|
||||
double costMinus = function.Gradient(gradient);
|
||||
|
||||
// Restore the parameter value.
|
||||
function.Parameters()(i) = tmp;
|
||||
|
||||
// Compute numerical gradients using the costs calculated above.
|
||||
estGradient(i) = (costPlus - costMinus) / (2 * eps);
|
||||
}
|
||||
|
||||
// Estimate error of gradient.
|
||||
return arma::norm(orgGradient - estGradient) /
|
||||
arma::norm(orgGradient + estGradient);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@
|
||||
* @file loss_functions_test.cpp
|
||||
* @author Dakshit Agrawal
|
||||
* @author Sourabh Varshney
|
||||
* @author Atharva Khandait
|
||||
*
|
||||
* Tests for loss functions in mlpack::methods::ann:loss_functions.
|
||||
*
|
||||
@@ -10,14 +11,21 @@
|
||||
* 3-clause BSD license along with mlpack. If not, see
|
||||
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
|
||||
*/
|
||||
#include <mlpack/methods/ann/loss_functions/earth_mover_distance.hpp>
|
||||
#include <mlpack/core.hpp>
|
||||
|
||||
#include <mlpack/methods/ann/layer/layer.hpp>
|
||||
#include <mlpack/methods/ann/loss_functions/kl_divergence.hpp>
|
||||
#include <mlpack/methods/ann/loss_functions/earth_mover_distance.hpp>
|
||||
#include <mlpack/methods/ann/loss_functions/mean_squared_error.hpp>
|
||||
#include <mlpack/methods/ann/loss_functions/sigmoid_cross_entropy_error.hpp>
|
||||
#include <mlpack/methods/ann/loss_functions/cross_entropy_error.hpp>
|
||||
#include <mlpack/methods/ann/loss_functions/reconstruction_loss.hpp>
|
||||
#include <mlpack/methods/ann/init_rules/nguyen_widrow_init.hpp>
|
||||
#include <mlpack/methods/ann/ffn.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "test_tools.hpp"
|
||||
#include "ann_test_tools.hpp"
|
||||
|
||||
using namespace mlpack;
|
||||
using namespace mlpack::ann;
|
||||
@@ -85,7 +93,7 @@ BOOST_AUTO_TEST_CASE(KLDivergenceNoMeanTest)
|
||||
/*
|
||||
* Simple test for the mean squared error performance function.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SimpleMeanSquaredErrorLayerTest)
|
||||
BOOST_AUTO_TEST_CASE(SimpleMeanSquaredErrorTest)
|
||||
{
|
||||
arma::mat input, output, target;
|
||||
MeanSquaredError<> module;
|
||||
@@ -120,7 +128,7 @@ BOOST_AUTO_TEST_CASE(SimpleMeanSquaredErrorLayerTest)
|
||||
/*
|
||||
* Simple test for the cross-entropy error performance function.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SimpleCrossEntropyErrorLayerTest)
|
||||
BOOST_AUTO_TEST_CASE(SimpleCrossEntropyErrorTest)
|
||||
{
|
||||
arma::mat input1, input2, output, target1, target2;
|
||||
CrossEntropyError<> module(1e-6);
|
||||
@@ -161,9 +169,9 @@ BOOST_AUTO_TEST_CASE(SimpleCrossEntropyErrorLayerTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple test for the Sigmoid Cross Entropy Layer.
|
||||
* Simple test for the Sigmoid Cross Entropy performance function.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SimpleSigmoidCrossEntropyLayerTest)
|
||||
BOOST_AUTO_TEST_CASE(SimpleSigmoidCrossEntropyErrorTest)
|
||||
{
|
||||
arma::mat input1, input2, input3, output, target1,
|
||||
target2, target3, expectedOutput;
|
||||
@@ -258,4 +266,47 @@ BOOST_AUTO_TEST_CASE(SimpleEarthMoverDistanceLayerTest)
|
||||
BOOST_REQUIRE_EQUAL(output.n_cols, input2.n_cols);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconstruction Loss numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientReconstructionLossTest)
|
||||
{
|
||||
// Linear function gradient instantiation.
|
||||
struct GradientFunction
|
||||
{
|
||||
GradientFunction()
|
||||
{
|
||||
input = arma::randu(10, 1);
|
||||
target = arma::randu(2, 1);
|
||||
|
||||
model = new FFN<ReconstructionLoss<>, NguyenWidrowInitialization>();
|
||||
model->Predictors() = input;
|
||||
model->Responses() = target;
|
||||
model->Add<IdentityLayer<> >();
|
||||
model->Add<Linear<> >(10, 2);
|
||||
model->Add<SigmoidLayer<> >();
|
||||
}
|
||||
|
||||
~GradientFunction()
|
||||
{
|
||||
delete model;
|
||||
}
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
}
|
||||
|
||||
arma::mat& Parameters() { return model->Parameters(); }
|
||||
|
||||
FFN<ReconstructionLoss<>, NguyenWidrowInitialization>* model;
|
||||
arma::mat input, target;
|
||||
} function;
|
||||
|
||||
BOOST_REQUIRE_LE(CheckGradient(function), 1e-4);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
Reference in New Issue
Block a user