From cf8fbaef8ee6539398b7c0a324d55bf911f7ad91 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Fri, 6 Dec 2019 11:31:28 +0800 Subject: [PATCH] update pruner and api doc. --- doc/prune_api.md | 100 +++++++++++++++++++++++++++++++++++-- paddleslim/prune/pruner.py | 17 +++++-- tests/test_prune.py | 2 +- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/doc/prune_api.md b/doc/prune_api.md index b931e453..a9a0ea7c 100644 --- a/doc/prune_api.md +++ b/doc/prune_api.md @@ -11,7 +11,7 @@ **参数:** - - **criterion:** 评估一个卷积层内通道重要性所参考的指标。目前仅支持`l1_norm`。默认为`l1_norm`。 +- **criterion:** 评估一个卷积层内通道重要性所参考的指标。目前仅支持`l1_norm`。默认为`l1_norm`。 **返回:** 一个Pruner类的实例 @@ -32,7 +32,7 @@ pruner = Pruner() - **program(paddle.fluid.Program):** 要裁剪的目标网络。更多关于Program的介绍请参考:[Program概念介绍](https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/fluid_cn/Program_cn.html#program)。 -- **scope(paddle.fluid.Scope):** 要裁剪的权重所在的`scope`,Paddle中用`scope`实例存放模型参数和运行时变量的值。更多介绍请参考[Scope概念介绍]() +- **scope(paddle.fluid.Scope):** 要裁剪的权重所在的`scope`,Paddle中用`scope`实例存放模型参数和运行时变量的值。Scope中的参数值会被`inplace`的裁剪。更多介绍请参考[Scope概念介绍]() - **params(list):** 需要被裁剪的卷积层的参数的名称列表。可以通过以下方式查看模型中所有参数的名称: ``` @@ -43,6 +43,100 @@ for block in program.blocks: - **ratios(list):** 用于裁剪`params`的剪切率,类型为列表。该列表长度必须与`params`的长度一致。 -- **place(paddle.fluid.Place):** +- **place(paddle.fluid.Place):** 待裁剪参数所在的设备位置,可以是`CUDAPlace`或`CPUPLace`。[Place概念介绍]() + +- **lazy(bool):** `lazy`为True时,通过将指定通道的参数置零达到裁剪的目的,参数的`shape保持不变`;`lazy`为False时,直接将要裁的通道的参数删除,参数的`shape`会发生变化。 + +- **only_graph(bool):** 是否只裁剪网络结构。在Paddle中,Program定义了网络结构,Scope存储参数的数值。一个Scope实例可以被多个Program使用,比如定义了训练网络的Program和定义了测试网络的Program是使用同一个Scope实例的。`only_graph`为True时,只对Program中定义的卷积的通道进行剪裁;`only_graph`为false时,Scope中卷积参数的数值也会被剪裁。默认为False。 + +- **param_backup(bool):** 是否返回对参数值的备份。默认为False。 + +- **param_shape_backup(bool):** 是否返回对参数`shape`的备份。 + +**返回:** + +- **pruned_program(paddle.fluid.Program):** 被裁剪后的Program。 + +- **param_backup(dict):** 对参数数值的备份,用于恢复Scope中的参数数值。 + +- **param_shape_backup(dict):** 对参数形状的备份。 + +**示例:** + +``` + +import paddle.fluid as fluid +from paddleslim.prune import Pruner + +def conv_bn_layer(input, + num_filters, + filter_size, + name, + stride=1, + groups=1, + act=None): + conv = fluid.layers.conv2d( + input=input, + num_filters=num_filters, + filter_size=filter_size, + stride=stride, + padding=(filter_size - 1) // 2, + groups=groups, + act=None, + param_attr=ParamAttr(name=name + "_weights"), + bias_attr=False, + name=name + "_out") + bn_name = name + "_bn" + return fluid.layers.batch_norm( + input=conv, + act=act, + name=bn_name + '_output', + param_attr=ParamAttr(name=bn_name + '_scale'), + bias_attr=ParamAttr(bn_name + '_offset'), + moving_mean_name=bn_name + '_mean', + moving_variance_name=bn_name + '_variance', ) + +main_program = fluid.Program() +startup_program = fluid.Program() +# X X O X O +# conv1-->conv2-->sum1-->conv3-->conv4-->sum2-->conv5-->conv6 +# | ^ | ^ +# |____________| |____________________| +# +# X: prune output channels +# O: prune input channels +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") + +place = fluid.CPUPlace() +exe = fluid.Executor(place) +scope = fluid.Scope() +exe.run(startup_program, scope=scope) +pruner = Pruner() +main_program, _, _ = pruner.prune( + main_program, + scope, + params=["conv4_weights"], + ratios=[0.5], + place=place, + lazy=False, + only_graph=False, + param_backup=None, + param_shape_backup=None) + +for param in main_program.global_block().all_parameters(): + if "weights" in param.name: + print("param name: {}; param shape: {}".format(param.name, param.shape)) + +``` + --- diff --git a/paddleslim/prune/pruner.py b/paddleslim/prune/pruner.py index 44b6b44c..95f6774c 100644 --- a/paddleslim/prune/pruner.py +++ b/paddleslim/prune/pruner.py @@ -41,8 +41,8 @@ class Pruner(): place=None, lazy=False, only_graph=False, - param_backup=None, - param_shape_backup=None): + param_backup=False, + param_shape_backup=False): """ Pruning the given parameters. Args: @@ -55,14 +55,18 @@ class Pruner(): False means cutting down the pruned elements. Default: False. only_graph(bool): True means only modifying the graph. False means modifying graph and variables in scope. Default: False. - param_backup(dict): A dict to backup the values of parameters. Default: None. - param_shape_backup(dict): A dict to backup the shapes of parameters. Default: None. + param_backup(bool): Whether to return a dict to backup the values of parameters. Default: False. + param_shape_backup(bool): Whether to return a dict to backup the shapes of parameters. Default: False. Returns: Program: The pruned program. + param_backup: A dict to backup the values of parameters. + param_shape_backup: A dict to backup the shapes of parameters. """ self.pruned_list = [] graph = GraphWrapper(program.clone()) + param_backup = {} if param_backup else None + param_shape_backup = {} if param_shape_backup else None self._prune_parameters( graph, scope, @@ -77,7 +81,7 @@ class Pruner(): if op.type() == 'depthwise_conv2d' or op.type( ) == 'depthwise_conv2d_grad': op.set_attr('groups', op.inputs('Filter')[0].shape()[0]) - return graph.program + return graph.program, param_backup, param_shape_backup def _prune_filters_by_ratio(self, scope, @@ -531,6 +535,9 @@ class Pruner(): self.pruned_list = [[], []] for param, ratio in zip(params, ratios): assert isinstance(param, str) or isinstance(param, unicode) + if param in self.pruned_list[0]: + _logger.info("Skip {}".format(param)) + continue _logger.info("pruning param: {}".format(param)) param = graph.var(param) self._forward_pruning_ralated_params( diff --git a/tests/test_prune.py b/tests/test_prune.py index 3fdaa867..931cf9cf 100644 --- a/tests/test_prune.py +++ b/tests/test_prune.py @@ -50,7 +50,7 @@ class TestPrune(unittest.TestCase): scope = fluid.Scope() exe.run(startup_program, scope=scope) pruner = Pruner() - main_program = pruner.prune( + main_program, _, _ = pruner.prune( main_program, scope, params=["conv4_weights"], -- GitLab