Skip to content

fix bug: wrong output dimension when "keep_dims" is false in pooling layer. #20904

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 9, 2021

Conversation

Crayon-new
Copy link
Contributor

@Crayon-new Crayon-new commented Oct 19, 2021

Merge with extra: opencv/opencv_extra#932

Pull Request Readiness Checklist

Fixed bug in #20896:When parsing a pooling layer in dnn/tensorflow. If keep_dims is false, the additional "nhwc" layer and "squeeze" layer will be lost. Because the final "squeeze" layer name is not the original layer name.

Solution:
Change the final output layer's name to the orignal pooling layer's name.

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on a code under GPL or other license that is incompatible with OpenCV
  • The PR is proposed to proper branch
  • There is reference to original bug report and related work
  • There is accuracy test, performance test and test data in opencv_extra repository, if applicable
    Patch to opencv_extra has the same branch name.
  • The feature is well documented and sample code can be built with the project CMake
opencv_extra=tf_pooling

@Crayon-new
Copy link
Contributor Author

Crayon-new commented Oct 19, 2021

I checked the build results. "Test_TensorFlow_layers.reduce_sum/0" failed because I connect a permute layer after "squeeze". So the output dimension from "nwc" change to "ncw".
(2I{ V~8%$2IDD{CG{F28VC

But I think it is necessary to add it. Because we may do some other operations (like "concat" in this model) after pooling.

Copy link
Member

@alalek alalek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for contribution!

connect(layer_id, dstNet, parsePin(layer.input(0)), id, 0);

if (!keepDims)
if (keepDims) {
Copy link
Member

@alalek alalek Oct 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test data sum_pool_by_axis_out.npy is generated by this code through original framework.

So changing the "output" only looks very strange (sum_pool_by_axis_out.npy in opencv_extra PR) and would conflict with framework results (we must avoid such conflicts).

Is there any information about processing/behavior changes between TF 1.x / TF 2.x?
If so we need to properly handle min_consumer TF versions here (but sum_pool_by_axis_net.pb file doesn't contain versions information).

Copy link
Contributor Author

@Crayon-new Crayon-new Oct 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your reply!
I think TF 1.x/ TF 2.x both produce the same output data.

@Crayon-new
Copy link
Contributor Author

@asmorkalov Hello, anything else do I need to do?

Copy link
Member

@rogday rogday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution! IIRC, bugfixes should go to 3.4 branch and not 4.x, so, please, rebase.

I think, there is more to it. Could you check other cases as well? I used +1 instead of ExpandDims to avoid more issues with dimensions.

I used the following code for tests, it shows errors with axis=[0], [3](keepdims true and false), [1,2]:

axises = [[0], [1], [2], [3], [1, 2]]

for axis in axises:
    for keepdims in [False, True]:
        inp = tf.placeholder(tf.float32, [2, 3, 4, 1])
        biasadd = tf.nn.bias_add(inp, [1], data_format='NHWC')
        print(axis, keepdims)
        reduced = tf.reduce_sum(biasadd, axis=axis, keepdims=keepdims)
        save(inp, reduced + 1, f'reduce_sum_{axis}_{keepdims}')
 
TEST_P(Test_TensorFlow_layers, pooling_reduce_sum3)
{
    std::vector<std::vector<int>> axises = {{0}, {1}, {2}, {3}, {1, 2}};

    for (const auto& axis : axises)
    {
        for (int keepdims = 0; keepdims <= 1; ++keepdims)
        {
            std::stringstream ss;
            ss << "reduce_sum_[" << axis[0];
            if (axis.size() > 1)
            {
                ss << ", " << axis[1];
            }
            ss << "]_" << (keepdims ? "True" : "False");
            std::cout << ss.str() << std::endl;
            try
            {
                runTensorFlowNet(ss.str());
            }
            catch (const std::exception& e)
            {
                std::cout << e.what() << std::endl;
            }
        }
    }
}

Comment on lines 2385 to 2240
layerParams.set("pool", pool_type);
layerParams.set(axis == 2 ? "kernel_w" : "kernel_h", 1);
layerParams.set(axis == 2 ? "global_pooling_h" : "global_pooling_w", true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this bit of code can be extracted from the if body.

@Crayon-new Crayon-new changed the base branch from 4.x to 3.4 November 8, 2021 11:27
Copy link
Member

@rogday rogday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work! 👍

{
// To keep correct order after squeeze dims we first need to change layout from NCHW to NHWC
std::string poolingName = name+"/Pooling";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add spaces around plus sign.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

addPermuteLayer(order, permName, inpId);

LayerParams squeezeLp;
std::string squeezeName = name + "/squeeze";
std::string squeezeName = name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, use const std::string&, since you aren't modifying the variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

int id = dstNet.addLayer(name, "Pooling", layerParams);
layer_id[name] = id;
connect(layer_id, dstNet, inpId, id, 0);
std::string poolingName = name+"/Pooling";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add spaces around the plus sign and a check, that the layer with that name doesn't exist yet: CV_Assert(layer_id.find(poolingName) == layer_id.end());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

addPermuteLayer(order, permName, inpId);

LayerParams squeezeLp;
std::string squeezeName = name + "/squeeze";
std::string squeezeName = name;
CV_Assert(layer_id.find(squeezeName) == layer_id.end());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is not required anymore(we aren't giving new name to the layer).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}
else
{
std::string poolingName = name+"/Pooling";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add spaces around the plus sign and a check, that the layer with that name doesn't exist yet: CV_Assert(layer_id.find(poolingName) == layer_id.end());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 2327 to 2328
std::string flattenName = name;
CV_Assert(layer_id.find(flattenName) == layer_id.end());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using const std::string& and removing the assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 2291 to 2292
std::string squeezeName = name;
CV_Assert(layer_id.find(squeezeName) == layer_id.end());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing the type to const std::string& and removing the assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 416 to 458
TEST_P(Test_TensorFlow_layers, pooling_reduce_sum2)
{
int axises[] = {0, 1, 2, 3};
for (int i = 0; i<sizeof(axises)/sizeof(axises[0]); i++)
{
for (int keepdims = 0; keepdims <= 1; ++keepdims)
{
std::stringstream ss;
ss << "reduce_sum_[" << axises[i] << "]_" << (keepdims ? "True" : "False");
std::cout << ss.str() << std::endl;
try
{
runTensorFlowNet(ss.str());
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
}
}
}

TEST_P(Test_TensorFlow_layers, pooling_reduce_sum3)
{
int axises[][2] = {{1, 2}}; // two axises
for (int i = 0; i<sizeof(axises)/sizeof(axises[0]); i++)
{
for (int keepdims = 0; keepdims <= 1; ++keepdims)
{
std::stringstream ss;
ss << "reduce_sum_[" << axises[i][0] << ", " << axises[i][1] << "]_" << (keepdims ? "True" : "False");
std::cout << ss.str() << std::endl;
try
{
runTensorFlowNet(ss.str());
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
}
}
}
Copy link
Member

@rogday rogday Nov 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My proposed solution was a draft, which helps debugging the problem.
Consider this instead:

TEST_P(Test_TensorFlow_layers, pooling_reduce_sum2)
{
    int axises[] = {0, 1, 2, 3};
    for (int keepdims = 0; keepdims <= 1; ++keepdims)
    {
        for (int i = 0; i < sizeof(axises)/sizeof(axises[0]); ++i)
        {
            runTensorFlowNet(cv::format("reduce_sum_[%d]_%s", axises[i], keepdims ? "True" : "False"));
        }
        runTensorFlowNet(cv::format("reduce_sum_[1, 2]_%s", keepdims ? "True" : "False"));
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

{
runTensorFlowNet(cv::format("reduce_sum_[%d]_%s", axises[i], (keepdims ? "True" : "False")));
}
runTensorFlowNet(cv::format("reduce_sum_[1, 2]_%s", keepdims ? "True" : "False"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'['
']'
','
' '

Could you please use sanitized file names? (please use _ instead of these symbols or space)

Copy link
Member

@rogday rogday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@alalek alalek merged commit 98b6ce3 into opencv:3.4 Nov 9, 2021
@alalek alalek mentioned this pull request Nov 13, 2021
@alalek alalek mentioned this pull request Dec 30, 2021
@alalek alalek mentioned this pull request Feb 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants