diff --git a/.gitignore b/.gitignore index 3f9835a0e603b01fde66b773f14b00effaaafbd3..e1f5c6f43a548fd6c3299aa606b506087558c854 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.pyc build/ ./dist/ +*.pyc +dist/ diff --git a/paddleslim/analysis/__init__.py b/paddleslim/analysis/__init__.py index 9d0531501ca43921438ee5b2fb58ac0ad2396d1b..9caa0d24006a3e59f2d39c646d247b7e68480f96 100644 --- a/paddleslim/analysis/__init__.py +++ b/paddleslim/analysis/__init__.py @@ -11,3 +11,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import flops as flops_module +from flops import * +import model_size as model_size_module +from model_size import * +__all__ = [] +__all__ += flops_module.__all__ +__all__ += model_size_module.__all__ diff --git a/paddleslim/analysis/flops.py b/paddleslim/analysis/flops.py new file mode 100644 index 0000000000000000000000000000000000000000..583c8e6ebf1a41f95c5ca8aeab0a1297cd798948 --- /dev/null +++ b/paddleslim/analysis/flops.py @@ -0,0 +1,68 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License" +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from ..core import GraphWrapper + +__all__ = ["flops"] + + +def flops(program): + """ + Get FLOPS of target graph. + Args: + program(Program): The program used to calculate FLOPS. + """ + graph = GraphWrapper(program) + return _graph_flops(graph) + + +def _graph_flops(graph, only_conv=False): + assert isinstance(graph, GraphWrapper) + flops = 0 + for op in graph.ops(): + if op.type() in ['conv2d', 'depthwise_conv2d']: + filter_shape = op.inputs("Filter")[0].shape() + input_shape = op.inputs("Input")[0].shape() + output_shape = op.outputs("Output")[0].shape() + c_out, c_in, k_h, k_w = filter_shape + _, _, h_out, w_out = output_shape + groups = op.attr("groups") + kernel_ops = k_h * k_w * (c_in / groups) + if len(op.inputs("Bias")) > 0: + with_bias = 1 + else: + with_bias = 0 + flops += 2 * h_out * w_out * c_out * (kernel_ops + with_bias) + elif op.type() == 'pool2d' and not only_conv: + input_shape = op.inputs("X")[0].shape() + output_shape = op.outputs("Out")[0].shape() + _, c_out, h_out, w_out = output_shape + k_size = op.attr("ksize") + flops += h_out * w_out * c_out * (k_size[0]**2) + + elif op.type() == 'mul' and not only_conv: + x_shape = list(op.inputs("X")[0].shape()) + y_shape = op.inputs("Y")[0].shape() + if x_shape[0] == -1: + x_shape[0] = 1 + flops += 2 * x_shape[0] * x_shape[1] * y_shape[1] + + elif op.type() in ['relu', 'sigmoid', 'batch_norm'] and not only_conv: + input_shape = list(op.inputs("X")[0].shape()) + if input_shape[0] == -1: + input_shape[0] = 1 + flops += np.product(input_shape) + + return flops diff --git a/paddleslim/analysis/model_size.py b/paddleslim/analysis/model_size.py new file mode 100644 index 0000000000000000000000000000000000000000..34574d5d53764810185112d7122aeb5b99d74682 --- /dev/null +++ b/paddleslim/analysis/model_size.py @@ -0,0 +1,31 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License" +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from ..core import GraphWrapper + +__all__ = ["model_size"] + + +def model_size(program): + """ + Get total value numbers of all parameters. + Args: + program(Program): The program used to calculate model size. + """ + size = 0 + for block in program.blocks: + for param in block.all_parameters(): + size += np.product(param.shape) + return size diff --git a/tests/test_flops.py b/tests/test_flops.py new file mode 100644 index 0000000000000000000000000000000000000000..cd16b8618d0271e6a0b7e609f8820e16c380b9db --- /dev/null +++ b/tests/test_flops.py @@ -0,0 +1,40 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License" +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import sys +sys.path.append("../") +import unittest +import paddle.fluid as fluid +from paddleslim.analysis import flops +from layers import conv_bn_layer + + +class TestPrune(unittest.TestCase): + def test_prune(self): + main_program = fluid.Program() + startup_program = fluid.Program() + with fluid.program_guard(main_program, startup_program): + input = fluid.data(name="image", shape=[None, 3, 16, 16]) + conv1 = conv_bn_layer(input, 8, 3, "conv1") + conv2 = conv_bn_layer(conv1, 8, 3, "conv2") + sum1 = conv1 + conv2 + conv3 = conv_bn_layer(sum1, 8, 3, "conv3") + conv4 = conv_bn_layer(conv3, 8, 3, "conv4") + sum2 = conv4 + sum1 + conv5 = conv_bn_layer(sum2, 8, 3, "conv5") + conv6 = conv_bn_layer(conv5, 8, 3, "conv6") + self.assertTrue(1597440 == flops(main_program)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_model_size.py b/tests/test_model_size.py new file mode 100644 index 0000000000000000000000000000000000000000..314450eb449507a971aa3827a8d841e88e10a69e --- /dev/null +++ b/tests/test_model_size.py @@ -0,0 +1,40 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License" +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import sys +sys.path.append("../") +import unittest +import paddle.fluid as fluid +from paddleslim.analysis import model_size +from layers import conv_bn_layer + + +class TestPrune(unittest.TestCase): + def test_prune(self): + main_program = fluid.Program() + startup_program = fluid.Program() + with fluid.program_guard(main_program, startup_program): + input = fluid.data(name="image", shape=[None, 3, 16, 16]) + conv1 = conv_bn_layer(input, 8, 3, "conv1") + conv2 = conv_bn_layer(conv1, 8, 3, "conv2") + sum1 = conv1 + conv2 + conv3 = conv_bn_layer(sum1, 8, 3, "conv3") + conv4 = conv_bn_layer(conv3, 8, 3, "conv4") + sum2 = conv4 + sum1 + conv5 = conv_bn_layer(sum2, 8, 3, "conv5") + conv6 = conv_bn_layer(conv5, 8, 3, "conv6") + self.assertTrue(3288 == model_size(main_program)) + + +if __name__ == '__main__': + unittest.main()