diff --git a/src/mlpack/methods/ann/layer/convolution_impl.hpp b/src/mlpack/methods/ann/layer/convolution_impl.hpp index 0e7deb2ef9..32d18994f8 100644 --- a/src/mlpack/methods/ann/layer/convolution_impl.hpp +++ b/src/mlpack/methods/ann/layer/convolution_impl.hpp @@ -403,19 +403,41 @@ void ConvolutionType< mappedError.slice(outMap + fullOutputOffset), rotatedFilters.slice(outMap), output, - 1, - 1, - strideWidth, - strideHeight); + strideHeight, + strideWidth); - if (usingPadding) + // If the stride width or height is greater than 1, then we have to + // insert columns and rows into the convolution output. + if (strideWidth == 1 && strideHeight == 1) { - gTemp.slice(inMap + fullInputOffset) += output.submat(padWLeft, - padHTop, padWLeft + gTemp.n_rows - 1, padHTop + gTemp.n_cols - 1); + if (usingPadding) + { + gTemp.slice(inMap + fullInputOffset) += output.submat( + padWLeft, + padHTop, + padWLeft + gTemp.n_rows - 1, + padHTop + gTemp.n_cols - 1); + } + else + { + gTemp.slice(inMap + fullInputOffset) += output; + } } else { - gTemp.slice(inMap + fullInputOffset) += output; + // We must iterate over each element of the output and manually + // re-insert the stride. + size_t col = padWLeft; + for (size_t i = 0; i < output.n_cols; ++i) + { + size_t row = padHTop; + for (size_t j = 0; j < output.n_rows; ++j) + { + gTemp(row, col, inMap + fullInputOffset) += output(j, i); + row += strideHeight; + } + col += strideWidth; + } } } } diff --git a/src/mlpack/tests/convolutional_network_test.cpp b/src/mlpack/tests/convolutional_network_test.cpp index 954e2d04c1..d89c3ae5bc 100644 --- a/src/mlpack/tests/convolutional_network_test.cpp +++ b/src/mlpack/tests/convolutional_network_test.cpp @@ -461,4 +461,36 @@ TEST_CASE("Issue2986", "[ConvolutionalNetworkTest]") REQUIRE_NOTHROW(c.Forward(input, output)); REQUIRE_NOTHROW(c.Backward(input, output, delta)); + + // Now test with a stride of 3. + c = Convolution(1, 3, 3, 3, 3, 0, 0); + + // Set up the layer without an enclosing FFN. + c.InputDimensions() = std::vector({ 6, 6 }); + c.ComputeOutputDimensions(); + weights.set_size(c.WeightSize(), 1); + weights.randu(); + c.SetWeights(weights.memptr()); + + output.set_size(c.OutputSize(), 1); + delta.set_size(input.size()); + + REQUIRE_NOTHROW(c.Forward(input, output)); + REQUIRE_NOTHROW(c.Backward(input, output, delta)); + + // Now test with different strides for height and width. + c = Convolution(1, 3, 3, 2, 3, 0, 0); + + // Set up the layer without an enclosing FFN. + c.InputDimensions() = std::vector({ 6, 6 }); + c.ComputeOutputDimensions(); + weights.set_size(c.WeightSize(), 1); + weights.randu(); + c.SetWeights(weights.memptr()); + + output.set_size(c.OutputSize(), 1); + delta.set_size(input.size()); + + REQUIRE_NOTHROW(c.Forward(input, output)); + REQUIRE_NOTHROW(c.Backward(input, output, delta)); }