Changing implementation and removing gradient function
This commit is contained in:
@@ -30,6 +30,8 @@
|
||||
* Fix incorrect neighbors for `k > 1` searches in `approx_kfn` binding, for
|
||||
the `QDAFN` algorithm (#2448).
|
||||
|
||||
* Add `RBF` layer in ann module to make `RBFN` architecture (#2261).
|
||||
|
||||
### mlpack 3.3.1
|
||||
###### 2020-04-29
|
||||
* Minor Julia and Python documentation fixes (#2373).
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace ann /** Artificial Neural Network. */ {
|
||||
* The gaussian function, defined by
|
||||
*
|
||||
* @f{eqnarray*}{
|
||||
* f(x) &=& \frac{1}{e^{-1 * x^2}} \\
|
||||
* f'(x) &=& f(x) * -x * 2)
|
||||
* f(x) = e^{-1 * x^2}
|
||||
* f'(x) = 2 * -x * f(x)
|
||||
* @f}
|
||||
*/
|
||||
class GaussianFunction
|
||||
@@ -55,7 +55,7 @@ class GaussianFunction
|
||||
/**
|
||||
* Computes the first derivative of the gaussian function.
|
||||
*
|
||||
* @param x Input data.
|
||||
* @param y Input data.
|
||||
* @return f'(x)
|
||||
*/
|
||||
static double Deriv(const double y)
|
||||
|
||||
@@ -267,7 +267,7 @@ using ElishFunctionLayer = BaseLayer<
|
||||
ActivationFunction, InputDataType, OutputDataType>;
|
||||
|
||||
/**
|
||||
* Standard ELiSH-Layer using the ELiSH activation function.
|
||||
* Standard Gaussian-Layer using the Gaussian activation function.
|
||||
*/
|
||||
template <
|
||||
class ActivationFunction = GaussianFunction,
|
||||
|
||||
@@ -82,7 +82,8 @@ template<typename InputDataType,
|
||||
class Linear;
|
||||
|
||||
template<typename InputDataType,
|
||||
typename OutputDataType>
|
||||
typename OutputDataType,
|
||||
typename Activation>
|
||||
class RBF;
|
||||
|
||||
template<typename InputDataType,
|
||||
@@ -215,7 +216,8 @@ using MoreTypes = boost::variant<
|
||||
Subview<arma::mat, arma::mat>*,
|
||||
VRClassReward<arma::mat, arma::mat>*,
|
||||
VirtualBatchNorm<arma::mat, arma::mat>*,
|
||||
RBF<arma::mat, arma::mat>*
|
||||
RBF<arma::mat, arma::mat, GaussianFunction>*,
|
||||
BaseLayer<GaussianFunction, arma::mat, arma::mat>*
|
||||
>;
|
||||
|
||||
template <typename... CustomLayers>
|
||||
@@ -234,7 +236,6 @@ using LayerTypes = boost::variant<
|
||||
BaseLayer<TanhFunction, arma::mat, arma::mat>*,
|
||||
BaseLayer<SoftplusFunction, arma::mat, arma::mat>*,
|
||||
BaseLayer<RectifierFunction, arma::mat, arma::mat>*,
|
||||
BaseLayer<GaussianFunction, arma::mat, arma::mat>*,
|
||||
BatchNorm<arma::mat, arma::mat>*,
|
||||
BilinearInterpolation<arma::mat, arma::mat>*,
|
||||
CELU<arma::mat, arma::mat>*,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#define MLPACK_METHODS_ANN_LAYER_RBF_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
#include <mlpack/methods/ann/regularizer/no_regularizer.hpp>
|
||||
#include <mlpack/methods/ann/activation_functions/gaussian_function.hpp>
|
||||
|
||||
#include "layer_types.hpp"
|
||||
|
||||
@@ -22,22 +22,33 @@ namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* Implementation of the Radial Basis Function layer. The RBF class when use with a
|
||||
* non-linear activation function acts as a Radial Basis Function which can be used
|
||||
* with Feed-Forward neural network.
|
||||
*
|
||||
* For more information, refer to the following paper,
|
||||
*
|
||||
* @code
|
||||
* @article{Volume 51: Artificial Intelligence and Statistics,
|
||||
* author = {Qichao Que, Mikhail Belkin},
|
||||
* title = {Back to the Future: Radial Basis Function Networks Revisited},
|
||||
* year = {2016},
|
||||
* url = {http://proceedings.mlr.press/v51/que16.pdf},
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @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 Activation Type of the activation function (mlpack::ann::Gaussian).
|
||||
*/
|
||||
|
||||
template <
|
||||
typename InputDataType = arma::mat,
|
||||
typename OutputDataType = arma::mat
|
||||
typename OutputDataType = arma::mat,
|
||||
typename Activation = GaussianFunction
|
||||
>
|
||||
class RBF
|
||||
{
|
||||
@@ -51,15 +62,13 @@ class RBF
|
||||
*
|
||||
* @param inSize The number of input units.
|
||||
* @param outSize The number of output units.
|
||||
* @param centres The centres calculated using k-means of data.
|
||||
* @param betas The beta value to be used with centres.
|
||||
*/
|
||||
RBF(const size_t inSize,
|
||||
const size_t outSize,
|
||||
arma::mat& centres);
|
||||
|
||||
/**
|
||||
* Reset the layer parameter.
|
||||
*/
|
||||
void Reset();
|
||||
arma::mat& centres,
|
||||
double betas = 0);
|
||||
|
||||
/**
|
||||
* Ordinary feed forward pass of the radial basis function.
|
||||
@@ -73,26 +82,11 @@ class RBF
|
||||
/**
|
||||
* Ordinary feed backward pass of the radial basis function.
|
||||
*
|
||||
* @param input The propagated input activation.
|
||||
* @param gy The backpropagated error.
|
||||
* @param g The calculated gradient.
|
||||
*/
|
||||
template<typename eT>
|
||||
void Backward(const arma::Mat<eT>& /* input */,
|
||||
const arma::Mat<eT>& gy,
|
||||
arma::Mat<eT>& g);
|
||||
|
||||
/*
|
||||
* Calculate the gradient using the output delta and the input activation.
|
||||
*
|
||||
* @param input The input parameter used for calculating the gradient.
|
||||
* @param error The calculated error.
|
||||
* @param gradient The calculated gradient.
|
||||
*/
|
||||
template<typename eT>
|
||||
void Gradient(const arma::Mat<eT>& input,
|
||||
const arma::Mat<eT>& error,
|
||||
arma::Mat<eT>& gradient);
|
||||
const arma::Mat<eT>& /* gy */,
|
||||
arma::Mat<eT>& /* g */);
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
@@ -123,33 +117,32 @@ class RBF
|
||||
void serialize(Archive& ar, const unsigned int /* version */);
|
||||
|
||||
private:
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
//! Locally-stored the learnable centre of the shape.
|
||||
InputDataType centres;
|
||||
|
||||
//! Locally-stored the learnable scaling factor of the shape.
|
||||
InputDataType sigmas;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored the output distances of the shape.
|
||||
OutputDataType distances;
|
||||
|
||||
//! Locally-stored reset parameter used to initialize the layer once.
|
||||
bool reset;
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
size_t inSize;
|
||||
|
||||
//! Locally-stored number of output units.
|
||||
size_t outSize;
|
||||
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
//! Locally-stored the sigmas values.
|
||||
double sigmas;
|
||||
|
||||
//! Locally-stored the betas values.
|
||||
double betas;
|
||||
|
||||
//! Locally-stored the learnable centre of the shape.
|
||||
InputDataType centres;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored the output distances of the shape.
|
||||
OutputDataType distances;
|
||||
}; // class RBF
|
||||
|
||||
} // namespace ann
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @file radial_basis_impl.hpp
|
||||
* @file radial_basis_function_impl.hpp
|
||||
* @author Himanshu Pathak
|
||||
*
|
||||
*
|
||||
@@ -12,50 +12,58 @@
|
||||
#define MLPACK_METHODS_ANN_LAYER_RBF_IMPL_HPP
|
||||
|
||||
// In case it hasn't yet been included.
|
||||
#include "dropout.hpp"
|
||||
#include "radial_basis_function.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
RBF<InputDataType, OutputDataType>::RBF() :
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename Activation>
|
||||
RBF<InputDataType, OutputDataType, Activation>::RBF() :
|
||||
inSize(0),
|
||||
outSize(0),
|
||||
reset(false)
|
||||
sigmas(0),
|
||||
betas(0)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
RBF<InputDataType, OutputDataType>::RBF(
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename Activation>
|
||||
RBF<InputDataType, OutputDataType, Activation>::RBF(
|
||||
const size_t inSize,
|
||||
const size_t outSize,
|
||||
arma::mat& centres) :
|
||||
arma::mat& centres,
|
||||
double betas) :
|
||||
inSize(inSize),
|
||||
outSize(outSize),
|
||||
centres(centres),
|
||||
reset(false)
|
||||
betas(betas),
|
||||
centres(centres)
|
||||
{
|
||||
sigmas = 0;
|
||||
if (betas == 0)
|
||||
{
|
||||
for (size_t i = 0; i < centres.n_cols; i++)
|
||||
{
|
||||
double max_dis = 0;
|
||||
arma::mat temp = centres.each_col() - centres.col(i);
|
||||
max_dis = arma::accu(arma::max(arma::pow(arma::sum(
|
||||
arma::pow((temp),
|
||||
2), 0), 0.5).t()));
|
||||
if (max_dis > sigmas)
|
||||
sigmas = max_dis;
|
||||
}
|
||||
this->betas = std::pow(2 * outSize, 0.5) / sigmas ;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
void RBF<InputDataType, OutputDataType>::Reset()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename Activation>
|
||||
template<typename eT>
|
||||
void RBF<InputDataType, OutputDataType>::Forward(
|
||||
void RBF<InputDataType, OutputDataType, Activation>::Forward(
|
||||
const arma::Mat<eT>& input,
|
||||
arma::Mat<eT>& output)
|
||||
{
|
||||
if(!reset)
|
||||
{
|
||||
arma::mat sigmas = arma::mat(1, outSize);
|
||||
sigmas.ones();
|
||||
sigmas = sigmas / outSize;
|
||||
reset = true;
|
||||
}
|
||||
distances = arma::mat(outSize, input.n_cols);
|
||||
|
||||
for (size_t i = 0; i < input.n_cols; i++)
|
||||
@@ -65,28 +73,29 @@ void RBF<InputDataType, OutputDataType>::Forward(
|
||||
arma::pow((temp),
|
||||
2), 0), 0.5).t();
|
||||
}
|
||||
|
||||
output = distances;
|
||||
Activation::Fn(distances * std::pow(betas, 0.5),
|
||||
output);
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename Activation>
|
||||
template<typename eT>
|
||||
void RBF<InputDataType, OutputDataType>::Backward(
|
||||
void RBF<InputDataType, OutputDataType, Activation>::Backward(
|
||||
const arma::Mat<eT>& /* input */,
|
||||
const arma::Mat<eT>& gy,
|
||||
arma::Mat<eT>& g)
|
||||
const arma::Mat<eT>& /* gy */,
|
||||
arma::Mat<eT>& /* g */)
|
||||
{
|
||||
g = centres.t() * gy;
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename Activation>
|
||||
template<typename Archive>
|
||||
void RBF<InputDataType, OutputDataType>::serialize(
|
||||
void RBF<InputDataType, OutputDataType, Activation>::serialize(
|
||||
Archive& ar,
|
||||
const unsigned int /* version */)
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(distances);
|
||||
ar & BOOST_SERIALIZATION_NVP(sigmas);
|
||||
ar & BOOST_SERIALIZATION_NVP(centres);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <mlpack/methods/ann/activation_functions/multi_quadratic_function.hpp>
|
||||
#include <mlpack/methods/ann/activation_functions/spline_function.hpp>
|
||||
#include <mlpack/methods/ann/activation_functions/poisson1_function.hpp>
|
||||
#include <mlpack/methods/ann/activation_functions/gaussian_function.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "test_tools.hpp"
|
||||
@@ -975,7 +976,7 @@ BOOST_AUTO_TEST_CASE(QuadraticFunctionTest)
|
||||
|
||||
CheckActivationCorrect<QuadraticFunction>(activationData, desiredActivations);
|
||||
CheckDerivativeCorrect<QuadraticFunction>(desiredActivations,
|
||||
desiredDerivatives);
|
||||
desiredDerivatives);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1040,4 +1041,29 @@ BOOST_AUTO_TEST_CASE(Poisson1FunctionTest)
|
||||
desiredDerivatives);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic test of the Gaussian activation function.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GaussianFunctionTest)
|
||||
{
|
||||
const arma::colvec desiredActivations("0.018315639 0.000035713 \
|
||||
1.6052280551856116e-09 \
|
||||
0 0.367879441 0.367879441 \
|
||||
0.018315639 1");
|
||||
|
||||
const arma::colvec desiredDerivatives("-0.036618991635992616 \
|
||||
-0.0000714259999 \
|
||||
-0.0000000032104561 \
|
||||
0 -0.6426287436 \
|
||||
-0.642628743680 \
|
||||
-0.03661899163 \
|
||||
-0.73575888234");
|
||||
|
||||
CheckActivationCorrect<GaussianFunction>(activationData,
|
||||
desiredActivations);
|
||||
CheckDerivativeCorrect<GaussianFunction>(desiredActivations,
|
||||
desiredDerivatives);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -628,6 +628,7 @@ BOOST_AUTO_TEST_CASE(OptimizerTest)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RBFNetworkTest)
|
||||
{
|
||||
mlpack::math::RandomSeed(std::time(NULL));
|
||||
// Load the dataset.
|
||||
arma::mat trainData;
|
||||
data::Load("thyroid_train.csv", trainData, true);
|
||||
@@ -635,6 +636,12 @@ BOOST_AUTO_TEST_CASE(RBFNetworkTest)
|
||||
arma::mat trainLabels = trainData.row(trainData.n_rows - 1);
|
||||
trainData.shed_row(trainData.n_rows - 1);
|
||||
|
||||
arma::mat trainLabels1 = arma::zeros(3, trainData.n_cols);
|
||||
for(size_t i = 0; i < trainData.n_cols; i++)
|
||||
{
|
||||
trainLabels1.col(i).row((trainLabels(i) - 1)) = 1;
|
||||
}
|
||||
|
||||
arma::mat testData;
|
||||
data::Load("thyroid_test.csv", testData, true);
|
||||
|
||||
@@ -659,12 +666,13 @@ BOOST_AUTO_TEST_CASE(RBFNetworkTest)
|
||||
KMeans<> kmeans;
|
||||
kmeans.Cluster(trainData, 8, centroids);
|
||||
|
||||
FFN<NegativeLogLikelihood<> > model;
|
||||
model.Add<RBF<> >(trainData.n_rows, 8, centroids);
|
||||
FFN<MeanSquaredError<> > model;
|
||||
model.Add<RBF<> >(trainData.n_rows, 8, centroids);
|
||||
model.Add<Linear<> >(8, 3);
|
||||
model.Add<LogSoftMax<> >();
|
||||
|
||||
TestNetwork<>(model, trainData, trainLabels, testData, testLabels, 10, 0.1);
|
||||
// RBFN neural net with MeanSquaredError.
|
||||
TestNetwork<>(model, trainData, trainLabels1, testData, testLabels, 10, 0.1);
|
||||
|
||||
arma::mat dataset;
|
||||
dataset.load("mnist_first250_training_4s_and_9s.arm");
|
||||
|
||||
@@ -676,20 +684,26 @@ BOOST_AUTO_TEST_CASE(RBFNetworkTest)
|
||||
|
||||
arma::mat labels = arma::zeros(1, dataset.n_cols);
|
||||
labels.submat(0, labels.n_cols / 2, 0, labels.n_cols - 1).fill(1);
|
||||
|
||||
arma::mat labels1 = arma::zeros(2, dataset.n_cols);
|
||||
for(size_t i = 0; i < dataset.n_cols; i++)
|
||||
{
|
||||
labels1.col(i).row(labels(i)) = 1;
|
||||
}
|
||||
labels += 1;
|
||||
|
||||
|
||||
|
||||
arma::mat centroids1;
|
||||
arma::Row<size_t> assignments;
|
||||
KMeans<> kmeans1;
|
||||
kmeans1.Cluster(dataset, 10, centroids1);
|
||||
kmeans1.Cluster(dataset, 140, centroids1);
|
||||
|
||||
FFN<NegativeLogLikelihood<> > model1;
|
||||
model1.Add<RBF<> >(dataset.n_rows, 10, centroids1);
|
||||
model.Add<Linear<> >(10, 2);
|
||||
model1.Add<LogSoftMax<> >();
|
||||
// Vanilla neural net with logistic activation function.
|
||||
TestNetwork<>(model1, dataset, labels, dataset, labels, 10, 0.3);
|
||||
FFN<MeanSquaredError<> > model1;
|
||||
model1.Add<RBF<> >(dataset.n_rows, 140, centroids1, 4.1);
|
||||
model1.Add<Linear<> >(140, 2);
|
||||
|
||||
// RBFN neural net with MeanSquaredError.
|
||||
TestNetwork<>(model1, dataset, labels1, dataset, labels, 10, 0.1);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
Reference in New Issue
Block a user