changes acc to suggestions
This commit is contained in:
@@ -28,38 +28,19 @@ namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
/**
|
||||
* Implementation of the Grouped Convolution class. The Grouped Convolution
|
||||
* class represents a single layer of a neural network.
|
||||
* Example usage:
|
||||
*
|
||||
* Suppose we want to pass a matrix M (2744x100) to a `Convolution` layer;
|
||||
* in this example, `M` was obtained from "flattening" 100 images (or Mel
|
||||
* cepstral coefficients, if we talk about speech, or whatever you like) of
|
||||
* dimension 196x14. In other words, the first 196 columns of each row of M
|
||||
* will be made of the 196 columns of the first row of each of the 100 images
|
||||
* (or Mel cepstral coefficients). Then the next 295 columns of M (196 - 393)
|
||||
* will be made of the 196 columns of the second row of the 100 images (or Mel
|
||||
* cepstral coefficients), etc. Given that the size of our 2-D input images is
|
||||
* 196x14, the parameters for our `Convolution` layer will be something like
|
||||
* this:
|
||||
*
|
||||
* ```
|
||||
* GroupedConvolution<> c(1, // Number of input activation maps.
|
||||
* 14, // Number of output activation maps.
|
||||
* 3, // Filter width.
|
||||
* 3, // Filter height.
|
||||
* 1, // Number of groups.
|
||||
* 1, // Stride along width.
|
||||
* 1, // Stride along height.
|
||||
* 0, // Padding width.
|
||||
* 0, // Padding height.
|
||||
* 196, // Input width.
|
||||
* 14); // Input height.
|
||||
* ```
|
||||
*
|
||||
* This `Convolution<>` layer will treat each column of the input matrix `M` as
|
||||
* a 2-D image (or object) of the original 196x14 size, using this as the input
|
||||
* for the 14 filters of this example.
|
||||
* Implementation of the Grouped Convolution class.
|
||||
*
|
||||
* For information on convolution, please refer to the convolution layer.
|
||||
*
|
||||
* A Grouped Convolution uses a group of convolutions - multiple kernels per
|
||||
* layer - resulting in multiple channel outputs per layer. This leads to wider
|
||||
* networks helping a network learn a varied set of low level and high level
|
||||
* features. The original motivation of using Grouped Convolutions in AlexNet
|
||||
* was to distribute the model over multiple GPUs as an engineering compromise.
|
||||
* But later, with models such as ResNeXt, it was shown this module could be
|
||||
* used to improve classification accuracy. Specifically by exposing a new
|
||||
* dimension through grouped convolutions, cardinality (the size of set of
|
||||
* transformations), we can increase accuracy by increasing it.
|
||||
*
|
||||
* The `groups` parameter controls the connections between inputs and outputs.
|
||||
* inMaps and outMaps must both be divisible by groups.
|
||||
@@ -70,6 +51,19 @@ namespace ann /** Artificial Neural Network. */ {
|
||||
* the output channels, and both subsequently concatenated.
|
||||
* At groups= inMaps, each input channel is convolved with its own set of
|
||||
* filters (of size \frac{\text{out\_channels}}{\text{in\_channels}}).
|
||||
*
|
||||
* For more information, kindly refer to the following paper.
|
||||
*
|
||||
* Paper for Grouped Convolution.
|
||||
*
|
||||
* @code
|
||||
* @article{Huang2018,
|
||||
* author = {Gao Huang, Shichen Liu, Laurens van der Maaten, Kilian Q. Weinberger},
|
||||
* title = {CondenseNet: An Efficient DenseNet Using Learned Group Convolutions},
|
||||
* year = {2018},
|
||||
* url = {https://openaccess.thecvf.com/content_cvpr_2018/papers/Huang_CondenseNet_An_Efficient_CVPR_2018_paper.pdf}
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @tparam ForwardConvolutionRule Convolution to perform forward process.
|
||||
* @tparam BackwardConvolutionRule Convolution to perform backward process.
|
||||
|
||||
@@ -608,8 +608,9 @@ void GroupedConvolutionType<
|
||||
|
||||
if ((inMaps % groups != 0) || (maps % groups != 0))
|
||||
{
|
||||
Log::Fatal << "Both input maps and output maps should be divisible by "
|
||||
<< "groups." << std::endl;
|
||||
Log::Fatal << "Both input maps (" << inMaps << ") and output maps ("
|
||||
<< maps << ") should be divisible by groups (" << groups << ")!"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// Compute and cache the total number of input maps.
|
||||
|
||||
@@ -68,6 +68,105 @@ TEST_CASE("GroupedConvolutionLayerTest", "[ANNLayerTest]")
|
||||
REQUIRE(arma::accu(delta) == Approx(686.7855224609).epsilon(1e-5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for testing equivalence of grouped convolution (groups = 1)
|
||||
* with convolution layer.
|
||||
*/
|
||||
TEST_CASE("GroupedConvolutionEquivalenceTest", "[ANNLayerTest]")
|
||||
{
|
||||
arma::mat input, output, outputG;
|
||||
|
||||
// The input test matrix is of the form 3 x 2 x 2 x 2 where
|
||||
// number of images are 3 and number of feature maps are 2.
|
||||
input = { { 1, 446, 42 },
|
||||
{ 2, 16, 63 },
|
||||
{ 3, 13, 63 },
|
||||
{ 4, 21, 21 },
|
||||
{ 1, 13, 11 },
|
||||
{ 32, 45, 42 },
|
||||
{ 22, 16 , 63 },
|
||||
{ 32, 13 , 42 } };
|
||||
|
||||
Convolution layer(2, 2, 2, 1, 1, 0, 0);
|
||||
layer.InputDimensions() = std::vector<size_t>({ 2, 2, 2 });
|
||||
layer.ComputeOutputDimensions();
|
||||
arma::mat layerWeights(layer.WeightSize(), 1);
|
||||
layerWeights(0) = 0.23757622;
|
||||
layerWeights(1) = -0.11899071;
|
||||
layerWeights(2) = 0.10450475;
|
||||
layerWeights(3) = -0.1303806;
|
||||
layerWeights(4) = -0.34706244;
|
||||
layerWeights(5) = -0.09472395;
|
||||
layerWeights(6) = 0.04117536;
|
||||
layerWeights(7) = -0.23012237;
|
||||
layerWeights(8) = -0.02827594;
|
||||
layerWeights(9) = -0.24280427;
|
||||
layerWeights(10) = 0.33375624;
|
||||
layerWeights(11) = -0.12285174;
|
||||
layerWeights(12) = -0.05546845;
|
||||
layerWeights(13) = -0.01502632;
|
||||
layerWeights(14) = -0.25894147;
|
||||
layerWeights(15) = -0.2283206;
|
||||
layerWeights(16) = 0.3204123974;
|
||||
layerWeights(17) = 0.2334779799;
|
||||
layer.SetWeights(layerWeights.memptr());
|
||||
output.set_size(layer.OutputSize(), 3);
|
||||
|
||||
GroupedConvolution layerG(2, 2, 2, 1, 1, 1, 0, 0);
|
||||
layerG.InputDimensions() = std::vector<size_t>({ 2, 2, 2 });
|
||||
layerG.ComputeOutputDimensions();
|
||||
arma::mat layerWeightsG(layerG.WeightSize(), 1);
|
||||
layerWeightsG(0) = 0.23757622;
|
||||
layerWeightsG(1) = -0.11899071;
|
||||
layerWeightsG(2) = 0.10450475;
|
||||
layerWeightsG(3) = -0.1303806;
|
||||
layerWeightsG(4) = -0.34706244;
|
||||
layerWeightsG(5) = -0.09472395;
|
||||
layerWeightsG(6) = 0.04117536;
|
||||
layerWeightsG(7) = -0.23012237;
|
||||
layerWeightsG(8) = -0.02827594;
|
||||
layerWeightsG(9) = -0.24280427;
|
||||
layerWeightsG(10) = 0.33375624;
|
||||
layerWeightsG(11) = -0.12285174;
|
||||
layerWeightsG(12) = -0.05546845;
|
||||
layerWeightsG(13) = -0.01502632;
|
||||
layerWeightsG(14) = -0.25894147;
|
||||
layerWeightsG(15) = -0.2283206;
|
||||
layerWeightsG(16) = 0.3204123974;
|
||||
layerWeightsG(17) = 0.2334779799;
|
||||
layerG.SetWeights(layerWeightsG.memptr());
|
||||
outputG.set_size(layerG.OutputSize(), 3);
|
||||
|
||||
layer.Forward(input, output);
|
||||
layerG.Forward(input, outputG);
|
||||
|
||||
// Value calculated using torch.nn.Conv2d().
|
||||
CheckMatrices(output, outputG);
|
||||
REQUIRE(arma::accu(output) == Approx(12.6755657196).epsilon(1e-5));
|
||||
REQUIRE(arma::accu(outputG) == Approx(12.6755657196).epsilon(1e-5));
|
||||
|
||||
arma::mat delta, deltaG;
|
||||
delta.set_size(8, 3);
|
||||
deltaG.set_size(8, 3);
|
||||
layer.Backward(input, output, delta);
|
||||
layerG.Backward(input, outputG, deltaG);
|
||||
|
||||
CheckMatrices(delta, deltaG);
|
||||
REQUIRE(arma::accu(delta) == Approx(-1.9237523079).epsilon(1e-5));
|
||||
REQUIRE(arma::accu(deltaG) == Approx(-1.9237523079).epsilon(1e-5));
|
||||
}
|
||||
|
||||
TEST_CASE("EdgeCaseFailGroupedConvolutionTest", "[ANNLayerTest]")
|
||||
{
|
||||
GroupedConvolution layer(2, 2, 2, 0, 1, 1, 0, 0);
|
||||
layer.InputDimensions() = std::vector<size_t>({ 2, 2, 2 });
|
||||
REQUIRE_THROWS(layer.ComputeOutputDimensions());
|
||||
|
||||
GroupedConvolution layer2(3, 2, 2, 2, 1, 1, 0, 0);
|
||||
layer2.InputDimensions() = std::vector<size_t>({ 2, 2, 2 });
|
||||
REQUIRE_THROWS(layer2.ComputeOutputDimensions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Grouped Convolution layer numerical gradient test.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user