Merge pull request #2739 from NippunSharma/iss2071
Adding clear errors to RNN's and FFN's when the input shape is wrong
This commit is contained in:
@@ -20,6 +20,7 @@ add_subdirectory(gan)
|
||||
add_subdirectory(rbm)
|
||||
add_subdirectory(augmented)
|
||||
add_subdirectory(regularizer)
|
||||
add_subdirectory(util)
|
||||
|
||||
# Add directory name to sources.
|
||||
set(DIR_SRCS)
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "visitor/set_input_height_visitor.hpp"
|
||||
#include "visitor/set_input_width_visitor.hpp"
|
||||
|
||||
#include "util/check_input_shape.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
@@ -109,6 +111,10 @@ double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
|
||||
OptimizerType& optimizer,
|
||||
CallbackTypes&&... callbacks)
|
||||
{
|
||||
CheckInputShape<std::vector<LayerTypes<CustomLayers...> > >(network,
|
||||
predictors.n_rows,
|
||||
"FFN<>::Train()");
|
||||
|
||||
ResetData(std::move(predictors), std::move(responses));
|
||||
|
||||
WarnMessageMaxIterations<OptimizerType>(optimizer, this->predictors.n_cols);
|
||||
@@ -131,6 +137,10 @@ double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
|
||||
arma::mat responses,
|
||||
CallbackTypes&&... callbacks)
|
||||
{
|
||||
CheckInputShape<std::vector<LayerTypes<CustomLayers...> > >(network,
|
||||
predictors.n_rows,
|
||||
"FFN<>::Train()");
|
||||
|
||||
ResetData(std::move(predictors), std::move(responses));
|
||||
|
||||
OptimizerType optimizer;
|
||||
@@ -217,6 +227,10 @@ template<typename OutputLayerType, typename InitializationRuleType,
|
||||
void FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Predict(
|
||||
arma::mat predictors, arma::mat& results)
|
||||
{
|
||||
CheckInputShape<std::vector<LayerTypes<CustomLayers...> > >(network,
|
||||
predictors.n_rows,
|
||||
"FFN<>::Predict()");
|
||||
|
||||
if (parameter.is_empty())
|
||||
ResetParameters();
|
||||
|
||||
@@ -250,6 +264,10 @@ template<typename PredictorsType, typename ResponsesType>
|
||||
double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Evaluate(
|
||||
const PredictorsType& predictors, const ResponsesType& responses)
|
||||
{
|
||||
CheckInputShape<std::vector<LayerTypes<CustomLayers...> > >(network,
|
||||
predictors.n_rows,
|
||||
"FFN<>::Evaluate()");
|
||||
|
||||
if (parameter.is_empty())
|
||||
ResetParameters();
|
||||
|
||||
|
||||
@@ -263,6 +263,12 @@ class AtrousConvolution
|
||||
return (outSize * inSize * kernelWidth * kernelHeight) + outSize;
|
||||
}
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inputHeight * inputWidth * inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
|
||||
@@ -118,6 +118,12 @@ class BilinearInterpolation
|
||||
//! Modify the depth of the input.
|
||||
size_t& InDepth() { return depth; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inRowSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
|
||||
@@ -259,6 +259,12 @@ class Convolution
|
||||
return (outSize * inSize * kernelWidth * kernelHeight) + outSize;
|
||||
}
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inputHeight * inputWidth * inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
|
||||
@@ -182,6 +182,12 @@ class FastLSTM
|
||||
return 4 * outSize * inSize + 4 * outSize + 4 * outSize * outSize;
|
||||
}
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
|
||||
@@ -182,6 +182,12 @@ class Glimpse
|
||||
//! Get the used glimpse size (height = width).
|
||||
size_t GlimpseSize() const { return size;}
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
|
||||
@@ -155,6 +155,12 @@ class GRU
|
||||
//! Get the number of output units.
|
||||
size_t OutSize() const { return outSize; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
|
||||
@@ -177,6 +177,12 @@ class Highway
|
||||
//! Get the number of input units.
|
||||
size_t InSize() const { return inSize; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
|
||||
@@ -148,6 +148,12 @@ class LayerNorm
|
||||
//! Get the value of epsilon.
|
||||
double Epsilon() const { return eps; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
|
||||
@@ -120,6 +120,10 @@ HAS_MEM_FUNC(Bias, HasBiasCheck);
|
||||
// we can use with SFINAE to catch when a type has a MaxIterations() function.
|
||||
HAS_MEM_FUNC(MaxIterations, HasMaxIterations);
|
||||
|
||||
// This gives us a HasInShapeCheck<T> type we can use with SFINAE to catch when
|
||||
// a type has a function named InputShape.
|
||||
HAS_ANY_METHOD_FORM(InputShape, HasInputShapeCheck);
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
|
||||
@@ -152,6 +152,12 @@ class Linear
|
||||
return (inSize * outSize) + outSize;
|
||||
}
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
|
||||
@@ -148,6 +148,12 @@ class Linear3D
|
||||
//! Modify the bias weights of the layer.
|
||||
OutputDataType& Bias() { return bias; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
|
||||
@@ -123,6 +123,12 @@ class LinearNoBias
|
||||
//! Modify the gradient.
|
||||
OutputDataType& Gradient() { return gradient; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
|
||||
@@ -183,6 +183,12 @@ class LSTM
|
||||
//! Get the number of output units.
|
||||
size_t OutSize() const { return outSize; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
|
||||
@@ -134,6 +134,12 @@ class MiniBatchDiscrimination
|
||||
//! Modify the gradient.
|
||||
OutputDataType& Gradient() { return gradient; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return A;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
|
||||
@@ -176,6 +176,11 @@ class MultiheadAttention
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
size_t InputShape() const
|
||||
{
|
||||
return embedDim * (tgtSeqLen + 2 * srcSeqLen);
|
||||
}
|
||||
|
||||
private:
|
||||
//! Element Type of the input.
|
||||
typedef typename OutputDataType::elem_type ElemType;
|
||||
|
||||
@@ -139,6 +139,12 @@ class NoisyLinear
|
||||
//! Modify the gradient.
|
||||
OutputDataType& Gradient() { return gradient; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inSize;
|
||||
}
|
||||
|
||||
//! Modify the bias weights of the layer.
|
||||
arma::mat& Bias() { return bias; }
|
||||
|
||||
|
||||
@@ -93,6 +93,11 @@ class PositionalEncoding
|
||||
//! Get the positional encoding vector.
|
||||
InputDataType const& Encoding() const { return positionalEncoding; }
|
||||
|
||||
size_t InputShape() const
|
||||
{
|
||||
return embedDim * maxSequenceLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
|
||||
@@ -110,6 +110,12 @@ class RBF
|
||||
//! Modify the delta.
|
||||
OutputDataType& Delta() { return delta; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "../visitor/delta_visitor.hpp"
|
||||
#include "../visitor/copy_visitor.hpp"
|
||||
#include "../visitor/output_parameter_visitor.hpp"
|
||||
#include "../visitor/input_shape_visitor.hpp"
|
||||
|
||||
#include "layer_types.hpp"
|
||||
#include "add_merge.hpp"
|
||||
@@ -138,6 +139,9 @@ class Recurrent
|
||||
//! Get the number of steps to backpropagate through time.
|
||||
size_t const& Rho() const { return rho; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const;
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "../visitor/backward_visitor.hpp"
|
||||
#include "../visitor/gradient_visitor.hpp"
|
||||
#include "../visitor/gradient_zero_visitor.hpp"
|
||||
#include "../visitor/input_shape_visitor.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
@@ -125,6 +126,53 @@ Recurrent<InputDataType, OutputDataType, CustomLayers...>::Recurrent(
|
||||
this->network.push_back(recurrentModule);
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename... CustomLayers>
|
||||
size_t Recurrent<InputDataType, OutputDataType, CustomLayers...>::InputShape() const
|
||||
{
|
||||
const size_t inputShapeStartModule = boost::apply_visitor(InShapeVisitor(), startModule);
|
||||
// Return the input shape of the first module that we have.
|
||||
if (inputShapeStartModule != 0)
|
||||
{
|
||||
return inputShapeStartModule;
|
||||
}
|
||||
// If input shape of first module is 0.
|
||||
else
|
||||
{
|
||||
// Return input shape of the second module that we have.
|
||||
const size_t inputShapeInputModule = boost::apply_visitor(InShapeVisitor(), inputModule);
|
||||
if (inputShapeInputModule != 0)
|
||||
{
|
||||
return inputShapeInputModule;
|
||||
// If the input shape of second module is 0.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return input shape of the third module that we have.
|
||||
const size_t inputShapeFeedbackModule = boost::apply_visitor(InShapeVisitor(),
|
||||
feedbackModule);
|
||||
if (inputShapeFeedbackModule != 0)
|
||||
{
|
||||
return inputShapeFeedbackModule;
|
||||
// If the input shape of the third module is 0.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return the shape of the fourth module that we have.
|
||||
const size_t inputShapeTransferModule = boost::apply_visitor(InShapeVisitor(),
|
||||
transferModule);
|
||||
if (inputShapeTransferModule != 0)
|
||||
{
|
||||
return inputShapeTransferModule;
|
||||
}
|
||||
// If the input shape of the fourth module is 0.
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename... CustomLayers>
|
||||
template<typename eT>
|
||||
|
||||
@@ -130,6 +130,11 @@ class Reparametrization
|
||||
//! Get the value of the beta hyperparameter.
|
||||
double Beta() const { return beta; }
|
||||
|
||||
size_t InputShape() const
|
||||
{
|
||||
return 2 * latentSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "../visitor/output_height_visitor.hpp"
|
||||
#include "../visitor/output_parameter_visitor.hpp"
|
||||
#include "../visitor/output_width_visitor.hpp"
|
||||
#include "../visitor/input_shape_visitor.hpp"
|
||||
|
||||
#include "layer_types.hpp"
|
||||
#include "add_merge.hpp"
|
||||
@@ -184,6 +185,8 @@ class Sequential
|
||||
//! Modify the gradient.
|
||||
arma::mat& Gradient() { return gradient; }
|
||||
|
||||
size_t InputShape() const;
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "../visitor/gradient_visitor.hpp"
|
||||
#include "../visitor/set_input_height_visitor.hpp"
|
||||
#include "../visitor/set_input_width_visitor.hpp"
|
||||
#include "../visitor/input_shape_visitor.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
@@ -94,6 +95,24 @@ Sequential<
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType, bool Residual,
|
||||
typename... CustomLayers>
|
||||
size_t Sequential<InputDataType, OutputDataType, Residual, CustomLayers...>::
|
||||
InputShape() const
|
||||
{
|
||||
size_t inputShape = 0;
|
||||
|
||||
for (size_t l = 0; l < network.size(); ++l)
|
||||
{
|
||||
if (inputShape == 0)
|
||||
inputShape = boost::apply_visitor(InShapeVisitor(), network[l]);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return inputShape;
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType, bool Residual,
|
||||
typename... CustomLayers>
|
||||
template<typename eT>
|
||||
|
||||
@@ -274,6 +274,12 @@ class TransposedConvolution
|
||||
//! Modify the right padding width.
|
||||
size_t& PadWRight() { return padWRight; }
|
||||
|
||||
//! Get the shape of the input.
|
||||
size_t InputShape() const
|
||||
{
|
||||
return inputHeight * inputWidth * inSize;
|
||||
}
|
||||
|
||||
//! Get the size of the weight matrix.
|
||||
size_t WeightSize() const
|
||||
{
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "visitor/gradient_visitor.hpp"
|
||||
#include "visitor/weight_set_visitor.hpp"
|
||||
|
||||
#include "util/check_input_shape.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
@@ -147,6 +149,10 @@ double RNN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
|
||||
OptimizerType& optimizer,
|
||||
CallbackTypes&&... callbacks)
|
||||
{
|
||||
CheckInputShape<std::vector<LayerTypes<CustomLayers...> > >(network,
|
||||
predictors.n_rows,
|
||||
"RNN<>::Train()");
|
||||
|
||||
numFunctions = responses.n_cols;
|
||||
|
||||
this->predictors = std::move(predictors);
|
||||
@@ -191,6 +197,10 @@ double RNN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
|
||||
arma::cube responses,
|
||||
CallbackTypes&&... callbacks)
|
||||
{
|
||||
CheckInputShape<std::vector<LayerTypes<CustomLayers...> > >(network,
|
||||
predictors.n_rows,
|
||||
"RNN<>::Train()");
|
||||
|
||||
numFunctions = responses.n_cols;
|
||||
|
||||
this->predictors = std::move(predictors);
|
||||
@@ -223,6 +233,10 @@ template<typename OutputLayerType, typename InitializationRuleType,
|
||||
void RNN<OutputLayerType, InitializationRuleType, CustomLayers...>::Predict(
|
||||
arma::cube predictors, arma::cube& results, const size_t batchSize)
|
||||
{
|
||||
CheckInputShape<std::vector<LayerTypes<CustomLayers...> > >(network,
|
||||
predictors.n_rows,
|
||||
"RNN<>::Predict()");
|
||||
|
||||
ResetCells();
|
||||
|
||||
if (parameter.is_empty())
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Define the files we need to compile
|
||||
# Anything not in this list will not be compiled into mlpack.
|
||||
set(SOURCES
|
||||
check_input_shape.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)
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file methods/ann/util/check_input_shape.hpp
|
||||
* @author Khizir Siddiqui
|
||||
* @author Nippun Sharma
|
||||
*
|
||||
* Definition of the CheckInputShape() function that checks
|
||||
* whether the shape of input is consistent with the first layer
|
||||
* of the neural network.
|
||||
*
|
||||
* 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_UTIL_CHECK_INPUT_SHAPE_HPP
|
||||
#define MLPACK_METHODS_ANN_UTIL_CHECK_INPUT_SHAPE_HPP
|
||||
|
||||
#include <mlpack/methods/ann/visitor/input_shape_visitor.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */{
|
||||
|
||||
template<typename T>
|
||||
void CheckInputShape(const T& network, const size_t inputShape,
|
||||
const std::string& functionName)
|
||||
{
|
||||
for (size_t l = 0; l < network.size(); ++l)
|
||||
{
|
||||
size_t layerInShape = boost::apply_visitor(InShapeVisitor(), network[l]);
|
||||
if (layerInShape == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (layerInShape == inputShape)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string estr = functionName + ": the first layer of the network " +
|
||||
"expects " + std::to_string(layerInShape) + " elements, but the " +
|
||||
"input has " + std::to_string(inputShape) + " dimensions!";
|
||||
throw std::logic_error(estr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -57,6 +57,8 @@ set(SOURCES
|
||||
weight_set_visitor_impl.hpp
|
||||
weight_size_visitor.hpp
|
||||
weight_size_visitor_impl.hpp
|
||||
input_shape_visitor.hpp
|
||||
input_shape_visitor_impl.hpp
|
||||
)
|
||||
|
||||
# Add directory name to sources.
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @file methods/ann/visitor/input_shape_visitor.hpp
|
||||
* @author Khizir Siddiqui
|
||||
* @author Nippun Sharma
|
||||
*
|
||||
* This file provides an abstraction for the InputShape() function for
|
||||
* different layers and automatically directs any parameter to the right layer
|
||||
* type.
|
||||
*
|
||||
* 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_VISITOR_INPUT_SHAPE_VISITOR_HPP
|
||||
#define MLPACK_METHODS_ANN_VISITOR_INPUT_SHAPE_VISITOR_HPP
|
||||
|
||||
#include <mlpack/methods/ann/layer/layer_traits.hpp>
|
||||
#include <mlpack/methods/ann/layer/layer_types.hpp>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann {
|
||||
|
||||
/**
|
||||
* InShapeVisitor returns the input shape a Layer expects.
|
||||
*/
|
||||
class InShapeVisitor : public boost::static_visitor<size_t>
|
||||
{
|
||||
public:
|
||||
//! Return the input shape of layer.
|
||||
template<typename LayerType>
|
||||
size_t operator()(LayerType* layer) const;
|
||||
|
||||
size_t operator()(MoreTypes layer) const;
|
||||
|
||||
private:
|
||||
//! If the module doesn't implement the InputShape() function return 0.
|
||||
template<typename T>
|
||||
typename std::enable_if<
|
||||
!HasInputShapeCheck<T>::value, size_t>::type
|
||||
LayerInputShape(T* layer) const;
|
||||
|
||||
//! If the module implements the InputShape() function returns the input shape.
|
||||
template<typename T>
|
||||
typename std::enable_if<
|
||||
HasInputShapeCheck<T>::value, size_t>::type
|
||||
LayerInputShape(T* layer) const;
|
||||
};
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
// Include implementation.
|
||||
#include "input_shape_visitor_impl.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @file methods/ann/visitor/input_shape_visitor_impl.hpp
|
||||
* @author Khizir Siddiqui
|
||||
* @author Nippun Sharma
|
||||
*
|
||||
* Implementation of the InputShape() function layer abstraction.
|
||||
*
|
||||
* 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_VISITOR_INPUT_SHAPE_VISITOR_IMPL_HPP
|
||||
#define MLPACK_METHODS_ANN_VISITOR_INPUT_SHAPE_VISITOR_IMPL_HPP
|
||||
|
||||
// In case it hasn't been included yet.
|
||||
#include "input_shape_visitor.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann {
|
||||
|
||||
//! InShapeVisitor visitor class.
|
||||
template<typename LayerType>
|
||||
inline std::size_t InShapeVisitor::operator()(LayerType* layer) const
|
||||
{
|
||||
return LayerInputShape(layer);
|
||||
}
|
||||
|
||||
inline std::size_t InShapeVisitor::operator()(MoreTypes layer) const
|
||||
{
|
||||
return layer.apply_visitor(*this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<
|
||||
!HasInputShapeCheck<T>::value, std::size_t>::type
|
||||
InShapeVisitor::LayerInputShape(T* /* layer */) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<
|
||||
HasInputShapeCheck<T>::value, std::size_t>::type
|
||||
InShapeVisitor::LayerInputShape(T* layer) const
|
||||
{
|
||||
return layer->InputShape();
|
||||
}
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -908,3 +908,39 @@ TEST_CASE("OptimizerTest", "[FeedForwardNetworkTest]")
|
||||
ens::DE opt(200, 1000, 0.6, 0.8, 1e-5);
|
||||
model.Train(trainData, trainLabels, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if an exception is thrown when input with
|
||||
* wrong shape is provided to a FFN.
|
||||
*/
|
||||
TEST_CASE("FFNCheckInputShapeTest", "[FeedForwardNetworkTest]")
|
||||
{
|
||||
// Load the dataset.
|
||||
arma::mat trainData;
|
||||
data::Load("thyroid_train.csv", trainData, true);
|
||||
|
||||
arma::mat trainLabels = trainData.row(trainData.n_rows - 1);
|
||||
trainData.shed_row(trainData.n_rows - 1);
|
||||
|
||||
arma::mat testData;
|
||||
data::Load("thyroid_test.csv", testData, true);
|
||||
|
||||
arma::mat testLabels = testData.row(testData.n_rows - 1);
|
||||
testData.shed_row(testData.n_rows - 1);
|
||||
|
||||
FFN<NegativeLogLikelihood<>, RandomInitialization, CustomLayer<> > model;
|
||||
// Purposely putting wrong input shape so that error is thrown.
|
||||
model.Add<Linear<> >(trainData.n_rows - 3, 8);
|
||||
model.Add<CustomLayer<> >();
|
||||
model.Add<Linear<> >(8, 3);
|
||||
model.Add<LogSoftMax<> >();
|
||||
|
||||
std::string expectedMsg = "FFN<>::Train(): ";
|
||||
expectedMsg += "the first layer of the network expects ";
|
||||
expectedMsg += std::to_string(trainData.n_rows - 3) + " elements, ";
|
||||
expectedMsg += "but the input has " + std::to_string(trainData.n_rows) + " dimensions! ";
|
||||
|
||||
ens::DE opt(200, 1000, 0.6, 0.8, 1e-5);
|
||||
|
||||
REQUIRE_THROWS_AS(model.Train(trainData, trainLabels, opt), std::logic_error);
|
||||
}
|
||||
|
||||
@@ -868,3 +868,66 @@ TEST_CASE("LargeRhoValueRnnTest", "[RecurrentNetworkTest]")
|
||||
model.Train(inputs[0], targets[0], opt);
|
||||
INFO("Training over");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to make sure that an error is thrown when input with
|
||||
* wrong input shape is provided to a RNN.
|
||||
*/
|
||||
TEST_CASE("RNNCheckInputShapeTest", "[RecurrentNetworkTest]")
|
||||
{
|
||||
const size_t rho = 10;
|
||||
|
||||
// Generate 12 (2 * 6) noisy sines. A single sine contains rho
|
||||
// points/features.
|
||||
arma::cube input;
|
||||
arma::mat labelsTemp;
|
||||
GenerateNoisySines(input, labelsTemp, rho, 6);
|
||||
|
||||
arma::cube labels = arma::zeros<arma::cube>(1, labelsTemp.n_cols, rho);
|
||||
for (size_t i = 0; i < labelsTemp.n_cols; ++i)
|
||||
{
|
||||
const int value = arma::as_scalar(arma::find(
|
||||
arma::max(labelsTemp.col(i)) == labelsTemp.col(i), 1)) + 1;
|
||||
labels.tube(0, i).fill(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a network with 1 input unit, 4 hidden units and 10 output
|
||||
* units. The hidden layer is connected to itself. The network structure
|
||||
* looks like:
|
||||
*
|
||||
* Input Hidden Output
|
||||
* Layer(1) Layer(4) Layer(10)
|
||||
* +-----+ +-----+ +-----+
|
||||
* | | | | | |
|
||||
* | +------>| +------>| |
|
||||
* | | ..>| | | |
|
||||
* +-----+ . +--+--+ +-----+
|
||||
* . .
|
||||
* . .
|
||||
* .......
|
||||
*/
|
||||
Add<> add(4);
|
||||
// Purposely providing wrong input shape of 3.
|
||||
// The correct input shape is 1.
|
||||
Linear<> lookup(3, 4);
|
||||
SigmoidLayer<> sigmoidLayer;
|
||||
Linear<> linear(4, 4);
|
||||
Recurrent<>* recurrent = new Recurrent<>(add, lookup, linear,
|
||||
sigmoidLayer, rho);
|
||||
|
||||
RNN<> model(rho);
|
||||
model.Add<IdentityLayer<> >();
|
||||
model.Add(recurrent);
|
||||
model.Add<Linear<> >(4, 10);
|
||||
model.Add<LogSoftMax<> >();
|
||||
|
||||
std::string expectedMsg = "RNN<>::Train(): ";
|
||||
expectedMsg += "the first layer of the network expects ";
|
||||
expectedMsg += std::to_string(3) + " elements, ";
|
||||
expectedMsg += "but the input has " + std::to_string(1) + " dimensions! ";
|
||||
|
||||
StandardSGD opt(0.1, 1, input.n_cols /* 1 epoch */, -100);
|
||||
|
||||
REQUIRE_THROWS_AS(model.Train(input, labels, opt), std::logic_error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user