Skip to content

Instantly share code, notes, and snippets.

@nilayjain
Last active July 26, 2016 19:55
Show Gist options
  • Save nilayjain/6cfbdade0214f77474a613c82a8a443c to your computer and use it in GitHub Desktop.
Save nilayjain/6cfbdade0214f77474a613c82a8a443c to your computer and use it in GitHub Desktop.
/**
* @file conv_layer.hpp
* @author Marcus Edel
*
* Definition of the ConvLayer class.
*/
#ifndef MLPACK_METHODS_ANN_LAYER_CONV_LAYER_HPP
#define MLPACK_METHODS_ANN_LAYER_CONV_LAYER_HPP
#include <mlpack/core.hpp>
#include <mlpack/methods/ann/layer/layer_traits.hpp>
#include <mlpack/methods/ann/convolution_rules/border_modes.hpp>
#include <mlpack/methods/ann/convolution_rules/naive_convolution.hpp>
namespace mlpack {
namespace ann /** Artificial Neural Network. */ {
/**
* Implementation of the ConvLayer class. The ConvLayer class represents a
* single layer of a neural network.
*
* @tparam ForwardConvolutionRule Convolution to perform forward process.
* @tparam BackwardConvolutionRule Convolution to perform backward process.
* @tparam GradientConvolutionRule Convolution to calculate gradient.
* @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).
*/
template <
typename ForwardConvolutionRule = NaiveConvolution<ValidConvolution>,
typename BackwardConvolutionRule = NaiveConvolution<FullConvolution>,
typename GradientConvolutionRule = NaiveConvolution<ValidConvolution>,
typename InputDataType = arma::cube,
typename OutputDataType = arma::cube
>
class ConvLayer
{
public:
/**
* Create the ConvLayer object using the specified number of input maps,
* output maps, filter size, stride and padding parameter.
*
* @param inMaps The number of input maps.
* @param outMaps The number of output maps.
* @param wfilter Width of the filter/kernel.
* @param hfilter Height of the filter/kernel.
* @param xStride Stride of filter application in the x direction.
* @param yStride Stride of filter application in the y direction.
* @param wPad Spatial padding width of the input.
* @param hPad Spatial padding height of the input.
*/
ConvLayer(const size_t inMaps,
const size_t outMaps,
const size_t wfilter,
const size_t hfilter,
const size_t xStride = 1,
const size_t yStride = 1,
const size_t wPad = 0,
const size_t hPad = 0) :
wfilter(wfilter),
hfilter(hfilter),
inMaps(inMaps),
outMaps(outMaps),
xStride(xStride),
yStride(yStride),
wPad(wPad),
hPad(hPad)
{
weights.set_size(wfilter, hfilter, inMaps * outMaps);
}
/**
* Ordinary feed forward pass of a neural network, evaluating the function
* f(x) by propagating the activity forward through f.
*
* @param input Input data used for evaluating the specified function.
* @param output Resulting output activation.
*/
template<typename eT>
void Forward(const arma::Cube<eT>& input, arma::Cube<eT>& output)
{
arma::Cube<eT> paddedInput;
bool usePaddedInput = false;
if (wPad != 0 || hPad != 0)
{
Pad(input, wPad, hPad, paddedInput);
usePaddedInput = true;
}
const size_t wConv = ConvOutSize(input.n_rows, wfilter, xStride, wPad);
const size_t hConv = ConvOutSize(input.n_cols, hfilter, yStride, hPad);
output = arma::zeros<arma::Cube<eT> >(wConv, hConv, outMaps);
for (size_t outMap = 0, outMapIdx = 0; outMap < outMaps; outMap++)
{
for (size_t inMap = 0; inMap < inMaps; inMap++, outMapIdx++)
{
arma::Mat<eT> convOutput;
if (usePaddedInput)
ForwardConvolutionRule::Convolution(paddedInput.slice(inMap),
weights.slice(outMapIdx), convOutput);
else
ForwardConvolutionRule::Convolution(input.slice(inMap),
weights.slice(outMapIdx), convOutput);
output.slice(outMap) += convOutput;
}
}
}
/**
* Ordinary feed backward pass of a neural network, calculating the function
* f(x) by propagating x backwards through f. Using the results from the feed
* forward pass.
*
* @param input The propagated input activation.
* @param gy The backpropagated error.
* @param g The calculated gradient.
*/
template<typename eT>
void Backward(const arma::Cube<eT>& /* unused */,
const arma::Cube<eT>& gy,
arma::Cube<eT>& g)
{
g = arma::zeros<arma::Cube<eT> >(inputParameter.n_rows,
inputParameter.n_cols,
inputParameter.n_slices);
for (size_t outMap = 0, outMapIdx = 0; outMap < inMaps; outMap++)
{
for (size_t inMap = 0; inMap < outMaps; inMap++, outMapIdx++)
{
arma::Mat<eT> rotatedFilter;
Rotate180(weights.slice(outMap * outMaps + inMap), rotatedFilter);
arma::Mat<eT> output;
BackwardConvolutionRule::Convolution(gy.slice(inMap), rotatedFilter,
output);
g.slice(outMap) += output.submat(rotatedFilter.n_rows / 2,
rotatedFilter.n_cols / 2,
rotatedFilter.n_rows / 2 + g.n_rows - 1,
rotatedFilter.n_cols / 2 + g.n_cols - 1);
}
}
}
/*
* Calculate the gradient using the output delta and the input activation.
*
* @param input The input parameter used for calculating the gradient.
* @param d The calculated error.
* @param g The calculated gradient.
*/
template<typename InputType, typename eT>
void Gradient(const InputType& input,
const arma::Cube<eT>& d,
arma::Cube<eT>& g)
{
g = arma::zeros<arma::Cube<eT> >(weights.n_rows, weights.n_cols,
weights.n_slices);
for (size_t outMap = 0; outMap < outMaps; outMap++)
{
for (size_t inMap = 0, s = outMap; inMap < inMaps; inMap++, s += outMaps)
{
arma::Cube<eT> inputSlices = input.slices(inMap, inMap);
arma::Cube<eT> deltaSlices = d.slices(outMap, outMap);
arma::Cube<eT> output;
GradientConvolutionRule::Convolution(inputSlices, deltaSlices, output);
for (size_t i = 0; i < output.n_slices; i++)
g.slice(s) += output.slice(i);
}
}
}
//! Get the weights.
OutputDataType const& Weights() const { return weights; }
//! Modify the weights.
OutputDataType& Weights() { return weights; }
//! Get the input parameter.
InputDataType const& InputParameter() const { return inputParameter; }
//! Modify the input parameter.
InputDataType& InputParameter() { return inputParameter; }
//! Get the output parameter.
OutputDataType const& OutputParameter() const { return outputParameter; }
//! Modify the output parameter.
OutputDataType& OutputParameter() { return outputParameter; }
//! Get the delta.
OutputDataType const& Delta() const { return delta; }
//! Modify the delta.
OutputDataType& Delta() { return delta; }
//! Get the gradient.
OutputDataType const& Gradient() const { return gradient; }
//! Modify the gradient.
OutputDataType& Gradient() { return gradient; }
/**
* Serialize the layer.
*/
template<typename Archive>
void Serialize(Archive& ar, const unsigned int /* version */)
{
ar & data::CreateNVP(weights, "weights");
ar & data::CreateNVP(wfilter, "wfilter");
ar & data::CreateNVP(hfilter, "hfilter");
ar & data::CreateNVP(inMaps, "inMaps");
ar & data::CreateNVP(outMaps, "outMaps");
ar & data::CreateNVP(xStride, "xStride");
ar & data::CreateNVP(yStride, "yStride");
ar & data::CreateNVP(wPad, "wPad");
ar & data::CreateNVP(hPad, "hPad");
}
private:
/*
* Rotates a 3rd-order tensor counterclockwise by 180 degrees.
*
* @param input The input data to be rotated.
* @param output The rotated output.
*/
template<typename eT>
void Rotate180(const arma::Cube<eT>& input, arma::Cube<eT>& output)
{
output = arma::Cube<eT>(input.n_rows, input.n_cols, input.n_slices);
// * left-right flip, up-down flip */
for (size_t s = 0; s < output.n_slices; s++)
output.slice(s) = arma::fliplr(arma::flipud(input.slice(s)));
}
/*
* Rotates a dense matrix counterclockwise by 180 degrees.
*
* @param input The input data to be rotated.
* @param output The rotated output.
*/
template<typename eT>
void Rotate180(const arma::Mat<eT>& input, arma::Mat<eT>& output)
{
// * left-right flip, up-down flip */
output = arma::fliplr(arma::flipud(input));
}
/*
* Return the convolution output size.
*
* @param size The size of the input (row or column).
* @param k The size of the filter (width or height).
* @param s The stride size (x or y direction).
* @param p The size of the padding (width or height).
* @return The convolution output size.
*/
size_t ConvOutSize(const size_t size,
const size_t k,
const size_t s,
const size_t p)
{
return std::floor(size + p * 2 - k) / s + 1;
}
template<typename eT>
void Pad(const arma::Mat<eT>& input, size_t wPad, size_t hPad, arma::Mat<eT>& output)
{
if (output.n_rows != input.n_rows + wPad * 2 ||
output.n_cols != input.n_cols + hPad * 2)
output = arma::zeros(input.n_rows + wPad * 2, input.n_cols + hPad * 2);
output.submat(wPad, hPad,
wPad + input.n_rows - 1,
hPad + input.n_cols - 1) = input;
}
template<typename eT>
void Pad(const arma::Cube<eT>& input, size_t wPad, size_t hPad, arma::Cube<eT>& output)
{
output = arma::zeros(input.n_rows + wPad * 2, input.n_cols + hPad * 2, input.n_slices);
for (size_t i = 0; i < input.n_slices; ++i)
Pad<double>(input.slice(i), wPad, hPad, output.slice(i));
}
//! Locally-stored filter/kernel width.
size_t wfilter;
//! Locally-stored filter/kernel height.
size_t hfilter;
//! Locally-stored number of input maps.
size_t inMaps;
//! Locally-stored number of output maps.
size_t outMaps;
//! Locally-stored stride of the filter in x-direction.
size_t xStride;
//! Locally-stored stride of the filter in y-direction.
size_t yStride;
//! Locally-stored padding width.
size_t wPad;
//! Locally-stored padding height.
size_t hPad;
//! Locally-stored weight object.
OutputDataType weights;
//! Locally-stored delta object.
OutputDataType delta;
//! Locally-stored gradient object.
OutputDataType gradient;
//! Locally-stored input parameter object.
InputDataType inputParameter;
//! Locally-stored output parameter object.
OutputDataType outputParameter;
}; // class ConvLayer
//! Layer traits for the convolution layer.
template<
typename ForwardConvolutionRule,
typename BackwardConvolutionRule,
typename GradientConvolutionRule,
typename InputDataType,
typename OutputDataType
>
class LayerTraits<ConvLayer<ForwardConvolutionRule,
BackwardConvolutionRule,
GradientConvolutionRule,
InputDataType,
OutputDataType> >
{
public:
static const bool IsBinary = false;
static const bool IsOutputLayer = false;
static const bool IsBiasLayer = false;
static const bool IsLSTMLayer = false;
static const bool IsConnection = true;
};
} // namespace ann
} // namespace mlpack
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment