From ac122a9ab73e9690faf4b9a696f725bead1e3836 Mon Sep 17 00:00:00 2001 From: Abhinav Anand Date: Wed, 26 May 2021 13:49:48 +0530 Subject: [PATCH 1/7] improved speed of mean_backward under certain condition --- src/mlpack/methods/ann/layer/mean_pooling.hpp | 85 ++++++++++++++----- 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/src/mlpack/methods/ann/layer/mean_pooling.hpp b/src/mlpack/methods/ann/layer/mean_pooling.hpp index 80daaa9951..312388eab1 100644 --- a/src/mlpack/methods/ann/layer/mean_pooling.hpp +++ b/src/mlpack/methods/ann/layer/mean_pooling.hpp @@ -195,35 +195,74 @@ class MeanPooling arma::Mat& output) { - arma::Mat unpooledError; - for (size_t j = 0, colidx = 0; j < input.n_cols; j += strideHeight, colidx++) + size_t condition = kernelHeight * kernelWidth - strideHeight * strideWidth - + kernelWidth - kernelHeight; + size_t kernalArea = kernelHeight * kernelWidth; + if (condition > 0) { - for (size_t i = 0, rowidx = 0; i < input.n_rows; i += strideWidth, rowidx++) - { - size_t rowEnd = i + kernelWidth - 1; - size_t colEnd = j + kernelHeight - 1; - - if (rowEnd > input.n_rows - 1) + for (size_t j = 0, colidx = 0; j < input.n_cols; j += strideHeight, colidx++) + { + for (size_t i = 0, rowidx = 0; i < input.n_rows; i += strideWidth, rowidx++) { - if (floor) - continue; - rowEnd = input.n_rows - 1; + size_t rowEnd = i + kernelWidth - 1; + size_t colEnd = j + kernelHeight - 1; + + if (rowEnd >= input.n_rows || colEnd >= input.n_cols) + break; + + output(i, j) += error(rowidx, colidx) / kernalArea; + + if (rowEnd + 1 < input.n_rows) + { + output(rowEnd + 1, j) -= error(rowidx, colidx) / kernalArea; + + if (colEnd + 1 < input.n_cols) + output(rowEnd + 1, colEnd + 1) += error(rowidx, colidx) / kernalArea; + } } - if (colEnd > input.n_cols - 1) + if (colEnd + 1 < input.n_cols) + output(i, colEnd + 1) -= error(rowidx, colidx) / kernalArea; + } + + for (size_t i = 1; i < input.n_rows; ++i) + output.row(i) += output.row(i - 1); + + for (size_t j = 1; j < input.n_cols; ++j) + output.col(j) += output.col(j - 1); + } + else + { + arma::Mat unpooledError; + for (size_t j = 0, colidx = 0; j < input.n_cols; j += strideHeight, colidx++) + { + for (size_t i = 0, rowidx = 0; i < input.n_rows; i += strideWidth, rowidx++) { - if (floor) - continue; - colEnd = input.n_cols - 1; + size_t rowEnd = i + kernelWidth - 1; + size_t colEnd = j + kernelHeight - 1; + + if (rowEnd > input.n_rows - 1) + { + if (floor) + continue; + rowEnd = input.n_rows - 1; + } + + if (colEnd > input.n_cols - 1) + { + if (floor) + continue; + colEnd = input.n_cols - 1; + } + + arma::mat InputArea = input(arma::span(i, rowEnd), arma::span(j, colEnd)); + + unpooledError = arma::Mat(InputArea.n_rows, InputArea.n_cols); + unpooledError.fill(error(rowidx, colidx) / InputArea.n_elem); + + output(arma::span(i, i + InputArea.n_rows - 1), + arma::span(j, j + InputArea.n_cols - 1)) += unpooledError; } - - arma::mat InputArea = input(arma::span(i, rowEnd), arma::span(j, colEnd)); - - unpooledError = arma::Mat(InputArea.n_rows, InputArea.n_cols); - unpooledError.fill(error(rowidx, colidx) / InputArea.n_elem); - - output(arma::span(i, i + InputArea.n_rows - 1), - arma::span(j, j + InputArea.n_cols - 1)) += unpooledError; } } } From b18199899b34ef5a5d76a986e9b9dfad26e5c193 Mon Sep 17 00:00:00 2001 From: Abhinav Anand Date: Wed, 26 May 2021 14:21:31 +0530 Subject: [PATCH 2/7] minor fix --- src/mlpack/methods/ann/layer/mean_pooling.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/ann/layer/mean_pooling.hpp b/src/mlpack/methods/ann/layer/mean_pooling.hpp index 312388eab1..bde20c3c6a 100644 --- a/src/mlpack/methods/ann/layer/mean_pooling.hpp +++ b/src/mlpack/methods/ann/layer/mean_pooling.hpp @@ -219,10 +219,10 @@ class MeanPooling if (colEnd + 1 < input.n_cols) output(rowEnd + 1, colEnd + 1) += error(rowidx, colidx) / kernalArea; } - } - if (colEnd + 1 < input.n_cols) - output(i, colEnd + 1) -= error(rowidx, colidx) / kernalArea; + if (colEnd + 1 < input.n_cols) + output(i, colEnd + 1) -= error(rowidx, colidx) / kernalArea; + } } for (size_t i = 1; i < input.n_rows; ++i) From 21744a71fd0bf9667baf7e697f0ae8ad71956645 Mon Sep 17 00:00:00 2001 From: abh2k <41710346+abh2k@users.noreply.github.com> Date: Wed, 26 May 2021 20:16:18 +0530 Subject: [PATCH 3/7] When ceil = true the kernal size will change. --- src/mlpack/methods/ann/layer/mean_pooling.hpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/ann/layer/mean_pooling.hpp b/src/mlpack/methods/ann/layer/mean_pooling.hpp index bde20c3c6a..1df12e5bc5 100644 --- a/src/mlpack/methods/ann/layer/mean_pooling.hpp +++ b/src/mlpack/methods/ann/layer/mean_pooling.hpp @@ -197,7 +197,7 @@ class MeanPooling size_t condition = kernelHeight * kernelWidth - strideHeight * strideWidth - kernelWidth - kernelHeight; - size_t kernalArea = kernelHeight * kernelWidth; + if (condition > 0) { for (size_t j = 0, colidx = 0; j < input.n_cols; j += strideHeight, colidx++) @@ -207,9 +207,21 @@ class MeanPooling size_t rowEnd = i + kernelWidth - 1; size_t colEnd = j + kernelHeight - 1; - if (rowEnd >= input.n_rows || colEnd >= input.n_cols) - break; + if (rowEnd > input.n_rows - 1) + { + if (floor) + continue; + rowEnd = input.n_rows - 1; + } + if (colEnd > input.n_cols - 1) + { + if (floor) + continue; + colEnd = input.n_cols - 1; + } + + size_t kernalArea = (rowEnd - i + 1) * (colEnd - j + 1); output(i, j) += error(rowidx, colidx) / kernalArea; if (rowEnd + 1 < input.n_rows) From d3952325128ef892c91c2bfd525993346dc63387 Mon Sep 17 00:00:00 2001 From: Abhinav Anand Date: Fri, 28 May 2021 08:37:47 +0530 Subject: [PATCH 4/7] Added comments to explain the method and minor style fix --- src/mlpack/methods/ann/layer/mean_pooling.hpp | 55 +++++++++++++++++-- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/src/mlpack/methods/ann/layer/mean_pooling.hpp b/src/mlpack/methods/ann/layer/mean_pooling.hpp index 1df12e5bc5..57c00b3f35 100644 --- a/src/mlpack/methods/ann/layer/mean_pooling.hpp +++ b/src/mlpack/methods/ann/layer/mean_pooling.hpp @@ -195,15 +195,60 @@ class MeanPooling arma::Mat& output) { - size_t condition = kernelHeight * kernelWidth - strideHeight * strideWidth - + const size_t condition = kernelHeight * kernelWidth - strideHeight * strideWidth - kernelWidth - kernelHeight; if (condition > 0) { - for (size_t j = 0, colidx = 0; j < input.n_cols; j += strideHeight, colidx++) + // If this condition is true then theoritically the prefix sum method of + // unpooling is faster. The aim of unpooling is to add + // `error(i, j) / kernalArea` to `inputArea(kernal)`. This requires + // inputArea.n_elem additions. So, total operations required will be + // `error.n_elem * inputArea.n_elem` operations. + // To improve this method we will use an idea of prefix sums. Let's see + // this method in 1-D matrix then we will extend it to 2-D matrix. + // Let the input be a 1-D matrix input = `[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]` of size 10 + // and we want to add `10` to idx = 1 to idx = 5. In brute force method we can run + // a loop from idx = 1 to idx = 5 and add `10` to each element. In prefix method + // We will add `+10` to idx = 1 and `-10` to idx = (5 + 1). Now the input will look + // like `[0, +10, 0, 0, 0, 0, -10, 0, 0, 0]`. After that we can just do prefix + // sum `input[i] += input[i - 1]`. Then the input becomes + // `[0, +10, +10, +10, +10, +10, 0, 0, 0, 0]`. So the total computation require + // by this method is (2 additions + Prefix operations). + // Note that if there are `k` such operation of adding a number of some + // continuous subarray. Then the brute force method will require + // `k * size(subarray)` operations. But the prefix method will require + // `2 * k + Prefix` operations, because the Prefix can be performed once at + // the end. + // Now for 2-D matrix. Lets say we want to add `e` to all elements from + // input(x1 : x2, y1 : y2). So the inputArea = (x2 - x1 + 1) * (y2 - y1 + 1). + // In prefix method the following operations will be performed: + // 1. Add `+e` to input(x1, y1). + // 2. Add `-e` to input(x1 + 1, y1). + // 3. Add `-e` to input(x1, y1 + 1). + // 4. Add `+e` to input(x1 + 1, y1 + 1). + // 5. Perform Prefix sum over columns i.e input(i, j) += input(i, j - 1) + // 6. Perform Prefix sum over rows i.e input(i, j) += input(i - 1, j) + // So lets say if we had `k` number of such operations. The brute force + // method will require `kernalArea * k` operations. + // The prefix method will require `4 * k + Prefix operation`. + + for (size_t j = 0, colidx = 0; j < input.n_cols; j += strideHeight, ++colidx) { - for (size_t i = 0, rowidx = 0; i < input.n_rows; i += strideWidth, rowidx++) + for (size_t i = 0, rowidx = 0; i < input.n_rows; i += strideWidth, ++rowidx) { + // We have to add error(i, j) to output(span(rowidx, rowEnd), span(colidx, colEnd)). + // The steps of prefix sum method: + // + // 1. For each (i, j) perform: + // 1.1 Add +error(i, j) to output(rowidx, colidx) + // 1.2 Add -error(i, j) to output(rowidx, colidx + 1) + // 1.3 Add -error(i, j) to output(rowidx + 1, colidx) + // 1.4 Add +error(i, j) to output(rowidx + 1, colidx + 1) + // + // 2. Do prefix sum column wise i.e output(i, j) += output(i, j - 1) + // 2. Do prefix sum row wise i.e output(i, j) += output(i - 1, j) + size_t rowEnd = i + kernelWidth - 1; size_t colEnd = j + kernelHeight - 1; @@ -246,9 +291,9 @@ class MeanPooling else { arma::Mat unpooledError; - for (size_t j = 0, colidx = 0; j < input.n_cols; j += strideHeight, colidx++) + for (size_t j = 0, colidx = 0; j < input.n_cols; j += strideHeight, ++colidx) { - for (size_t i = 0, rowidx = 0; i < input.n_rows; i += strideWidth, rowidx++) + for (size_t i = 0, rowidx = 0; i < input.n_rows; i += strideWidth, ++rowidx) { size_t rowEnd = i + kernelWidth - 1; size_t colEnd = j + kernelHeight - 1; From d063002f1f3c56998803052b4b51f7a345d9944c Mon Sep 17 00:00:00 2001 From: abh2k <41710346+abh2k@users.noreply.github.com> Date: Sun, 6 Jun 2021 15:41:52 +0530 Subject: [PATCH 5/7] Got a better condition expression and gave its explanation --- src/mlpack/methods/ann/layer/mean_pooling.hpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/ann/layer/mean_pooling.hpp b/src/mlpack/methods/ann/layer/mean_pooling.hpp index a5e97c9585..3d7b0716b4 100644 --- a/src/mlpack/methods/ann/layer/mean_pooling.hpp +++ b/src/mlpack/methods/ann/layer/mean_pooling.hpp @@ -210,10 +210,18 @@ class MeanPooling const arma::Mat& error, arma::Mat& output) { - const size_t condition = kernelHeight * kernelWidth - strideHeight * strideWidth - - kernelWidth - kernelHeight; + // This condition comes by comparing the number of operations involved in the brute + // force method and the prefix method. Let the area of error be errorArea and area + // of kernal be kernalArea. Total number of operations in brute force method will be + // `errorArea * kernalArea` and for each element in error we are doing kernalArea + // number of operations. Whereas in the prefix method the total number of operations + // will be `4 * errorArea + 2 * inputArea`. The term `2 * inputArea` comes from + // prefix sums performed (col-wise and row-wise). + // We can use this to determine which method to use. + const bool condition = (error.n_elem * kernalHeight * kernalWidth) > + (4 * error.n_elem + 2 * input.n_elem); - if (condition > 0) + if (condition) { // If this condition is true then theoritically the prefix sum method of // unpooling is faster. The aim of unpooling is to add From 01cb4b92b755fa881f3f4acfaf090c0870b7b14b Mon Sep 17 00:00:00 2001 From: abh2k <41710346+abh2k@users.noreply.github.com> Date: Sun, 6 Jun 2021 20:28:33 +0530 Subject: [PATCH 6/7] typo fix. --- src/mlpack/methods/ann/layer/mean_pooling.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/ann/layer/mean_pooling.hpp b/src/mlpack/methods/ann/layer/mean_pooling.hpp index 3d7b0716b4..ef0150653f 100644 --- a/src/mlpack/methods/ann/layer/mean_pooling.hpp +++ b/src/mlpack/methods/ann/layer/mean_pooling.hpp @@ -218,7 +218,7 @@ class MeanPooling // will be `4 * errorArea + 2 * inputArea`. The term `2 * inputArea` comes from // prefix sums performed (col-wise and row-wise). // We can use this to determine which method to use. - const bool condition = (error.n_elem * kernalHeight * kernalWidth) > + const bool condition = (error.n_elem * kernelHeight * kernelWidth) > (4 * error.n_elem + 2 * input.n_elem); if (condition) From c0d5fef5472ff72a9ac8a174f27a712d9e328d97 Mon Sep 17 00:00:00 2001 From: abh2k <41710346+abh2k@users.noreply.github.com> Date: Mon, 7 Jun 2021 09:25:58 +0530 Subject: [PATCH 7/7] Apply suggestions from code review Co-authored-by: Marcus Edel --- src/mlpack/methods/ann/layer/mean_pooling.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mlpack/methods/ann/layer/mean_pooling.hpp b/src/mlpack/methods/ann/layer/mean_pooling.hpp index ef0150653f..7789247018 100644 --- a/src/mlpack/methods/ann/layer/mean_pooling.hpp +++ b/src/mlpack/methods/ann/layer/mean_pooling.hpp @@ -213,7 +213,7 @@ class MeanPooling // This condition comes by comparing the number of operations involved in the brute // force method and the prefix method. Let the area of error be errorArea and area // of kernal be kernalArea. Total number of operations in brute force method will be - // `errorArea * kernalArea` and for each element in error we are doing kernalArea + // `errorArea * kernalArea` and for each element in error we are doing `kernalArea` // number of operations. Whereas in the prefix method the total number of operations // will be `4 * errorArea + 2 * inputArea`. The term `2 * inputArea` comes from // prefix sums performed (col-wise and row-wise). @@ -226,7 +226,7 @@ class MeanPooling // If this condition is true then theoritically the prefix sum method of // unpooling is faster. The aim of unpooling is to add // `error(i, j) / kernalArea` to `inputArea(kernal)`. This requires - // inputArea.n_elem additions. So, total operations required will be + // `inputArea.n_elem` additions. So, total operations required will be // `error.n_elem * inputArea.n_elem` operations. // To improve this method we will use an idea of prefix sums. Let's see // this method in 1-D matrix then we will extend it to 2-D matrix.