diff --git a/src/mlpack/methods/ann/convolution_rules/native_atrous_convolution.hpp b/src/mlpack/methods/ann/convolution_rules/native_atrous_convolution.hpp new file mode 100644 index 0000000000..3b8ea06715 --- /dev/null +++ b/src/mlpack/methods/ann/convolution_rules/native_atrous_convolution.hpp @@ -0,0 +1,79 @@ +/** + * @file naive_atrous_convolution.hpp + * @author Aarush Gupta + * + * Implementation of atrous convolution. + * + * 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_CONVOLUTION_RULES_NAIVE_ATROUS_CONVOLUTION_HPP +#define MLPACK_METHODS_ANN_CONVOLUTION_RULES_NAIVE_ATROUS_CONVOLUTION_HPP + +#include +#include "border_modes.hpp" + +namespace mlpack { +namespace ann /** Artificial Neural Network. */ { + +/** + * Computes the two-dimensional convolution. Only ValidConvolution border mode + * is supported. + * + * ValidConvolution: returns the valid two-dimensional convolution. + * + * + * @tparam BorderMode Type of the border mode (only ValidConvolution + * supported for now). + * + */ +template +class NativeConvolution +{ + public: + /* + * Perform a convolution. + * + * @param input Input used to perform the convolution. + * @param filter Filter used to perform the conolution. + * @param output Output data that contains the results of the convolution. + * @param dW Stride of filter application in the x direction. + * @param dH Stride of filter application in the y direction. + * @param dilation the dilation factor in both x and y directions. + */ + template + static typename std::enable_if< + std::is_same::value, void>::type + Convolution(const arma::Mat& input, + const arma::Mat& filter, + arma::Mat& output, + const size_t dW = 1, + const size_t dH = 1, + const size_t dilation = 0) + { + output = arma::zeros >((input.n_rows - ((filter.n_rows-1)*dilation + filter.n_rows) + 1) / + dW, (input.n_cols - ((filter.n_cols-1)*dilation + filter.n_cols)+1) / dH); + + + + eT* outputPtr = output.memptr(); + + for (size_t j = 0; j < output.n_cols; ++j) + { + for (size_t i = 0; i < output.n_rows; ++i, outputPtr++) + { + const eT* kernelPtr = filter.memptr(); + for (size_t kj = 0; kj < filter.n_cols; kj++) + { + const eT* inputPtr = input.colptr(kj + j * dW + kj*(dilation+1)) + i * dH; + for ( size_t ki = 0; ki < filter.n_rows; ++kernelPtr, ++inputPtr) + *outputPtr += *kernelPtr * (*inputPtr); + ki+=dilation+1; + } + } + } + } + +} \ No newline at end of file diff --git a/src/mlpack/methods/ann/layer/atrous_convolution.hpp b/src/mlpack/methods/ann/layer/atrous_convolution.hpp new file mode 100644 index 0000000000..fe7bff0112 --- /dev/null +++ b/src/mlpack/methods/ann/layer/atrous_convolution.hpp @@ -0,0 +1,359 @@ +/** + * @file atrous_convolution.hpp + * @author Aarush Gupta + * + * Definition of the Atrous Convolution module 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_LAYER_ATROUS_CONVOLUTION_HPP +#define MLPACK_METHODS_ANN_LAYER_ATROUS_CONVOLUTION_HPP + +#include + +#include +#include + +// Not including fft and svd convolution as of now + +#include "layer_types.hpp" + +namespace mlpack{ +namespace ann /** Artificial Neural Network. */ { + +/** + * Implementation of the Atrous Convolution class. The Atrous Convolution + * class represents a single layer of a neural network. + * + * @tparam ForwardConvolutionRule Atrous Convolution to perform forward process. + * @tparam BackwardConvolutionRule Atrous Convolution to perform backward process. + * @tparam GradientConvolutionRule Atrous 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, + typename BackwardConvolutionRule = NaiveConvolution, + typename GradientConvolutionRule = NaiveConvolution, + typename InputDataType = arma::mat, + typename OutputDataType = arma::mat +> + +class AtrousConvolution +{ + public: + //! Create the AtrousConvolution object. + AtrousConvolution(); + + /** + * Create the AtrousConvolution object using the specified number of + * input maps, output maps, filter size, stride, dilation and + * padding parameter. + * + * @param inSize The number of input maps. + * @param outSize The number of output maps. + * @param kW Width of the filter/kernel. + * @param kH Height of the filter/kernel. + * @param dW Stride of filter application in the x direction. + * @param dH Stride of filter application in the y direction. + * @param padW Padding width of the input. + * @param padH Padding height of the input. + * @param inputWidth The widht of the input data. + * @param inputHeight The height of the input data. + * @param dilation The amount of space between the cells of the filters. + */ + AtrousConvolution(const size_t inSize, + const size_t outSize, + const size_t kW, + const size_t kH, + const size_t dW = 1, + const size_t dH = 1, + const size_t padW = 0, + const size_t padH = 0, + const size_t inputWidth = 0, + const size_t inputHeight = 0, + const size_t dilation = 0); + + /* + * Set the weight and bias term. + */ + void Reset(); + + /** + * 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 + void Forward(const arma::Mat&& input, arma::Mat&& output); + + /** + * 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 + void Backward(const arma::Mat&& /* input */, + arma::Mat&& gy, + arma::Mat&& 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 + void Gradient(const arma::Mat&& /* input */, + arma::Mat&& error, + arma::Mat&& gradient); + + //! Get the parameters. + OutputDataType const& Parameters() const { return weights; } + //! Modify the parameters. + OutputDataType& Parameters() { 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; } + + //! Get the input width. + size_t const& InputWidth() const { return inputWidth; } + //! Modify input the width. + size_t& InputWidth() { return inputWidth; } + + //! Get the input height. + size_t const& InputHeight() const { return inputHeight; } + //! Modify the input height. + size_t& InputHeight() { return inputHeight; } + + //! Get the output width. + size_t const& OutputWidth() const { return outputWidth; } + //! Modify the output width. + size_t& OutputWidth() { return outputWidth; } + + //! Get the output height. + size_t const& OutputHeight() const { return outputHeight; } + //! Modify the output height. + size_t& OutputHeight() { return outputHeight; } + + //! Get the dilation. + size_t const& Dilation() const { return dilation; } + //! Modify the dilation. + size_t& Dilation() { return dilation; } + + /** + * Serialize the layer + */ + template + void serialize(Archive& ar, const unsigned int /* version */); + + private: + /* + * 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). + * @param d The dilation size. + * @return The convolution output size. + */ + size_t ConvOutSize( const size_t size, + const size_t k, + const size_t s, + const size_t p, + const size_t d) + { + return std::floor(size + p * 2 - (k + d*(k - 1)))/s + 1; + } + + /* + * Rotates a 3rd-order tensor counterclockwise by 180 degrees. + * + * @param input The input data to be rotated. + * @param output The rotated output. + */ + template + void Rotate180(const arma::Cube& input, arma::Cube& output) + { + output = arma::Cube(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 + void Rotate180(const arma::Mat& input, arma::Mat& output) + { + // * left-right flip, up-down flip */ + output = arma::fliplr(arma::flipud(input)); + } + + /* + * Pad the given input data. + * + * @param input The input to be padded. + * @param wPad Padding width of the input. + * @param hPad Padding height of the input. + * @param output The padded output data. + */ + template + void Pad(const arma::Mat& input, + size_t wPad, + size_t hPad, + arma::Mat& 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; + } + + /* + * Pad the given input data. + * + * @param input The input to be padded. + * @param wPad Padding width of the input. + * @param hPad Padding height of the input. + * @param output The padded output data. + */ + template + void Pad(const arma::Cube& input, + size_t wPad, + size_t hPad, + arma::Cube& 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(input.slice(i), wPad, hPad, output.slice(i)); + } + } + + //! Locally-stored number of input units. + size_t inSize; + + //! Locally-stored number of output units. + size_t outSize; + + //! Locally-stored filter/kernel width. + size_t kW; + + //! Locally-stored filter/kernel height. + size_t kH; + + //! Locally-stored stride of the filter in x-direction. + size_t dW; + + //! Locally-stored stride of the filter in y-direction. + size_t dH; + + //! Locally-stored padding width. + size_t padW; + + //! Locally-stored padding height. + size_t padH; + + //! Locally-stored weight object. + OutputDataType weights; + + //! Locally-stored weight object. + arma::cube weight; + + //! Locally-stored bias term object. + arma::mat bias; + + //! Locally-stored input width. + size_t inputWidth; + + //! Locally-stored input height. + size_t inputHeight; + + //! Locally-stored output width. + size_t outputWidth; + + //! Locally-stored output height. + size_t outputHeight; + + //! Locally-stored dilation factor + size_t dilation; + + //! Locally-stored transformed output parameter. + arma::cube outputTemp; + + //! Locally-stored transformed input parameter. + arma::cube inputTemp; + + //! Locally-stored transformed padded input parameter. + arma::cube inputPaddedTemp; + + //! Locally-stored transformed error parameter. + arma::cube gTemp; + + //! Locally-stored transformed gradient parameter. + arma::cube gradientTemp; + + //! 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 AtrousConvolution + + + +} // namespace ann +} // namespace mlpack + +// Include implementation +#include "atrous_convolution_impl.hpp" + +#endif \ No newline at end of file diff --git a/src/mlpack/methods/ann/layer/atrous_convolution_impl.hpp b/src/mlpack/methods/ann/layer/atrous_convolution_impl.hpp new file mode 100644 index 0000000000..123fee12a8 --- /dev/null +++ b/src/mlpack/methods/ann/layer/atrous_convolution_impl.hpp @@ -0,0 +1,202 @@ +/** + * @file atrous_convolution_impl.hpp + * @author Aarush Gupta + * + * Implementation of the Atrous Convolution module 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_LAYER_ATROUS_CONVOLUTION_IMPL_HPP +#define MLPACK_METHODS_ANN_LAYER_ATROUS_CONVOLUTION_IMPL_HPP + +// In case it hasn't yet been included. +#include "atrous_convolution.hpp" + +namespace mlpack { +namespace ann /** Artificial Neural Network. */ { + +template< + typename ForwardConvolutionRule, + typename BackwardConvolutionRule, + typename GradientConvolutionRule, + typename InputDataType, + typename OutputDataType +> +Convolution< + ForwardConvolutionRule, + BackwardConvolutionRule, + GradientConvolutionRule, + InputDataType, + OutputDataType +>::Convolution() +{ + // Nothing to do here. +} + +template< + typename ForwardConvolutionRule, + typename BackwardConvolutionRule, + typename GradientConvolutionRule, + typename InputDataType, + typename OutputDataType +> +Convolution< + ForwardConvolutionRule, + BackwardConvolutionRule, + GradientConvolutionRule, + InputDataType, + OutputDataType +>::Convolution( + const size_t inSize, + const size_t outSize, + const size_t kW, + const size_t kH, + const size_t dW, + const size_t dH, + const size_t padW, + const size_t padH, + const size_t inputWidth, + const size_t inputHeight, + const size_t dilation) : + inSize(inSize), + outSize(outSize), + kW(kW), + kH(kH), + dW(dW), + dH(dH), + padW(padW), + padH(padH), + inputWidth(inputWidth), + inputHeight(inputHeight), + outputWidth(0), + outputHeight(0), + dilation(dilation) +{ + weights.set_size((outSize * inSize * kW * kH) + outSize, 1); +} + +template< + typename ForwardConvolutionRule, + typename BackwardConvolutionRule, + typename GradientConvolutionRule, + typename InputDataType, + typename OutputDataType +> +void Convolution< + ForwardConvolutionRule, + BackwardConvolutionRule, + GradientConvolutionRule, + InputDataType, + OutputDataType +>::Reset() +{ + weight = arma::cube(weights.memptr(), kW, kH, + outSize * inSize, false, false); + bias = arma::mat(weights.memptr() + weight.n_elem, + outSize, 1, false, false); +} + +template< + typename ForwardConvolutionRule, + typename BackwardConvolutionRule, + typename GradientConvolutionRule, + typename InputDataType, + typename OutputDataType +> +template +void Convolution< + ForwardConvolutionRule, + BackwardConvolutionRule, + GradientConvolutionRule, + InputDataType, + OutputDataType +>::Forward(const arma::Mat&& input, arma::Mat&& output) +{ + inputTemp = arma::cube(input.memptr(), inputWidth, inputHeight, inSize); + + if (padW != 0 || padH != 0) + { + Pad(inputTemp, padW, padH, inputPaddedTemp); + } + + size_t wConv = ConvOutSize(inputWidth, kW, dW, padW, dilation); + size_t hConv = ConvOutSize(inputHeight, kH, dH, padH, dilation); + + outputTemp = arma::zeros >(wConv, hConv, outSize); + + for (size_t outMap = 0, outMapIdx = 0; outMap < outSize; outMap++) + { + for (size_t inMap = 0; inMap < inSize; inMap++, outMapIdx++) + { + arma::Mat convOutput; + + if (padW != 0 || padH != 0) + { + ForwardConvolutionRule::Convolution(inputPaddedTemp.slice(inMap), + weight.slice(outMapIdx), convOutput, dW, dH, dilation); + } + else + { + ForwardConvolutionRule::Convolution(inputTemp.slice(inMap), + weight.slice(outMapIdx), convOutput, dW, dH, dilation); + } + + outputTemp.slice(outMap) += convOutput; + } + + outputTemp.slice(outMap) += bias(outMap); + } + + output = arma::Mat(outputTemp.memptr(), outputTemp.n_elem, 1); + + outputWidth = outputTemp.n_rows; + outputHeight = outputTemp.n_cols; +} + +} + + + + +template< + typename ForwardConvolutionRule, + typename BackwardConvolutionRule, + typename GradientConvolutionRule, + typename InputDataType, + typename OutputDataType +> +template +void Convolution< + ForwardConvolutionRule, + BackwardConvolutionRule, + GradientConvolutionRule, + InputDataType, + OutputDataType +>::serialize( + Archive& ar, const unsigned int /* version */) +{ + ar & BOOST_SERIALIZATION_NVP(inSize); + ar & BOOST_SERIALIZATION_NVP(outSize); + ar & BOOST_SERIALIZATION_NVP(kW); + ar & BOOST_SERIALIZATION_NVP(kH); + ar & BOOST_SERIALIZATION_NVP(dW); + ar & BOOST_SERIALIZATION_NVP(dH); + ar & BOOST_SERIALIZATION_NVP(padW); + ar & BOOST_SERIALIZATION_NVP(padH); + ar & BOOST_SERIALIZATION_NVP(inputWidth); + ar & BOOST_SERIALIZATION_NVP(inputHeight); + ar & BOOST_SERIALIZATION_NVP(outputWidth); + ar & BOOST_SERIALIZATION_NVP(outputHeight); + ar & BOOST_SERIALIZATION_NVP(dilation); + + if (Archive::is_loading::value) + weights.set_size((outSize * inSize * kW * kH) + outSize, 1); +} + +} // namespace ann +} // namespace mlpack + +#endif