• 卷积层通道剪裁


    卷积层通道剪裁

    Pruner

    classpaddleslim.prune.Pruner(criterion="l1_norm")

    对卷积网络的通道进行一次剪裁。剪裁一个卷积层的通道,是指剪裁该卷积层输出的通道。卷积层的权重形状为 [output_channel, input_channel, kernel_size, kernel_size] ,通过剪裁该权重的第一纬度达到剪裁输出通道数的目的。

    参数:

    • criterion - 评估一个卷积层内通道重要性所参考的指标。目前仅支持 l1_norm 。默认为 l1_norm 。

    返回: 一个Pruner类的实例

    示例代码:

    from paddleslim.prune import Pruner

    pruner = Pruner()

    paddleslim.prune.Pruner.prune(programscopeparamsratiosplace=Nonelazy=Falseonly_graph=Falseparam_backup=Falseparam_shape_backup=False)

    对目标网络的一组卷积层的权重进行裁剪。

    参数:

    • program(paddle.fluid.Program) - 要裁剪的目标网络。更多关于Program的介绍请参考:Program概念介绍
    • scope(paddle.fluid.Scope) - 要裁剪的权重所在的 scope ,Paddle中用 scope 实例存放模型参数和运行时变量的值。Scope中的参数值会被 inplace 的裁剪。
    • params(list<str>) - 需要被裁剪的卷积层的参数的名称列表。可以通过以下方式查看模型中所有参数的名称:

    for block in program.blocks:

        for param in block.all_parameters():

            print("param: {}; shape: {}".format(param.name, param.shape))

    • ratios(list<float>) - 用于裁剪 params 的剪切率,类型为列表。该列表长度必须与 params 的长度一致。
    • 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 的备份。默认为False。

    返回:

    • pruned_program(paddle.fluid.Program) - 被裁剪后的Program。
    • param_backup(dict) - 对参数数值的备份,用于恢复Scope中的参数数值。
    • param_shape_backup(dict) - 对参数形状的备份。

    示例:

    执行以下示例代码。

    import paddle.fluid as fluid

    from paddle.fluid.param_attr import ParamAttr

    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=False,

        param_shape_backup=False)

     

    for param in main_program.global_block().all_parameters():

        if "weights" in param.name:

            print("param name: {}; param shape: {}".format(param.name, param.shape))

    sensitivity

    paddleslim.prune.sensitivity(programplaceparam_nameseval_funcsensitivities_file=Nonepruned_ratios=None)

    计算网络中每个卷积层的敏感度。每个卷积层的敏感度信息统计方法为:依次剪掉当前卷积层不同比例的输出通道数,在测试集上计算剪裁后的精度损失。得到敏感度信息后,可以通过观察或其它方式确定每层卷积的剪裁率。

    参数:

    • program(paddle.fluid.Program) - 待评估的目标网络。更多关于Program的介绍请参考:Program概念介绍
    • place(paddle.fluid.Place) - 待分析的参数所在的设备位置,可以是 CUDAPlace 或 CPUPlace 。[Place概念介绍]()
    • param_names(list<str>) - 待分析的卷积层的参数的名称列表。可以通过以下方式查看模型中所有参数的名称:
    • eval_func(function) - 用于评估裁剪后模型效果的回调函数。该回调函数接受被裁剪后的 program 为参数,返回一个表示当前program的精度,用以计算当前裁剪带来的精度损失。
    • sensitivities_file(str) - 保存敏感度信息的本地文件系统的文件。在敏感度计算过程中,会持续将新计算出的敏感度信息追加到该文件中。重启任务后,文件中已有敏感度信息不会被重复计算。该文件可以用 pickle 加载。
    • pruned_ratios(list<float>) - 计算卷积层敏感度信息时,依次剪掉的通道数比例。默认为 [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] 。

    返回:

    • sensitivities(dict) - 存放敏感度信息的dict,其格式为:

    {"weight_0":

       {0.1: 0.22,

        0.2: 0.33

       },

     "weight_1":

       {0.1: 0.21,

        0.2: 0.4

       }

    }

    其中, weight_0 是卷积层参数的名称, sensitivities['weight_0'] 的 value 为剪裁比例, value 为精度损失的比例。

    示例:

    点击 AIStudio 运行以下示例代码。

    import paddle

    import numpy as np

    import paddle.fluid as fluid

    from paddle.fluid.param_attr import ParamAttr

    from paddleslim.prune import sensitivity

    import paddle.dataset.mnist as reader

     

    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

    image_shape = [1,28,28]

    with fluid.program_guard(main_program, startup_program):

        image = fluid.data(name='image', shape=[None]+image_shape, dtype='float32')

        label = fluid.data(name='label', shape=[None, 1], dtype='int64')

        conv1 = conv_bn_layer(image, 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")

        out = fluid.layers.fc(conv6, size=10, act="softmax")

    #    cost = fluid.layers.cross_entropy(input=out, label=label)

    #    avg_cost = fluid.layers.mean(x=cost)

        acc_top1 = fluid.layers.accuracy(input=out, label=label, k=1)

    #    acc_top5 = fluid.layers.accuracy(input=out, label=label, k=5)

     

     

    place = fluid.CPUPlace()

    exe = fluid.Executor(place)

    exe.run(startup_program)

     

    val_reader = paddle.batch(reader.test(), batch_size=128)

    val_feeder = feeder = fluid.DataFeeder(

            [image, label], place, program=main_program)

     

    def eval_func(program):

     

        acc_top1_ns = []

        for data in val_reader():

            acc_top1_n = exe.run(program,

                                 feed=val_feeder.feed(data),

                                 fetch_list=[acc_top1.name])

            acc_top1_ns.append(np.mean(acc_top1_n))

        return np.mean(acc_top1_ns)

    param_names = []

    for param in main_program.global_block().all_parameters():

        if "weights" in param.name:

            param_names.append(param.name)

    sensitivities = sensitivity(main_program,

                                place,

                                param_names,

                                eval_func,

                                sensitivities_file="./sensitive.data",

                                pruned_ratios=[0.1, 0.2, 0.3])

    print(sensitivities)

    merge_sensitive

    paddleslim.prune.merge_sensitive(sensitivities)

    合并多个敏感度信息。

    参数:

    • sensitivities(list<dict> | list<str>) - 待合并的敏感度信息,可以是字典的列表,或者是存放敏感度信息的文件的路径列表。

    返回:

    • sensitivities(dict) - 合并后的敏感度信息。其格式为:

    {"weight_0":

       {0.1: 0.22,

        0.2: 0.33

       },

     "weight_1":

       {0.1: 0.21,

        0.2: 0.4

       }

    }

    其中, weight_0 是卷积层参数的名称, sensitivities['weight_0'] 的 value 为剪裁比例, value 为精度损失的比例。

    示例:

    from paddleslim.prune import merge_sensitive

    sen0 = {"weight_0":

       {0.1: 0.22,

        0.2: 0.33

       },

     "weight_1":

       {0.1: 0.21,

        0.2: 0.4

       }

    }

    sen1 = {"weight_0":

       {0.3: 0.41,

       },

     "weight_2":

       {0.1: 0.10,

        0.2: 0.35

       }

    }

    sensitivities = merge_sensitive([sen0, sen1])

    print(sensitivities)

    load_sensitivities

    paddleslim.prune.load_sensitivities(sensitivities_file)

    从文件中加载敏感度信息。

    参数:

    • sensitivities_file(str) - 存放敏感度信息的本地文件.

    返回:

    • sensitivities(dict) - 敏感度信息。

    示例:

    import pickle

    from paddleslim.prune import load_sensitivities

    sen = {"weight_0":

       {0.1: 0.22,

        0.2: 0.33

       },

     "weight_1":

       {0.1: 0.21,

        0.2: 0.4

       }

    }

    sensitivities_file = "sensitive_api_demo.data"

    with open(sensitivities_file, 'wb') as f:

        pickle.dump(sen, f)

    sensitivities = load_sensitivities(sensitivities_file)

    print(sensitivities)

    get_ratios_by_loss

    paddleslim.prune.get_ratios_by_loss(sensitivitiesloss)

    根据敏感度和精度损失阈值计算出一组剪切率。对于参数 w , 其剪裁率为使精度损失低于 loss 的最大剪裁率。

    参数:

    • sensitivities(dict) - 敏感度信息。
    • loss - 精度损失阈值。

    返回:

    • ratios(dict) - 一组剪切率。 key 是待剪裁参数的名称。 value 是对应参数的剪裁率。

    示例:

    from paddleslim.prune import get_ratios_by_loss

    sen = {"weight_0":

       {0.1: 0.22,

        0.2: 0.33

       },

     "weight_1":

       {0.1: 0.21,

        0.2: 0.4

       }

    }

     

    ratios = get_ratios_by_loss(sen, 0.3)

    print(ratios)

    人工智能芯片与自动驾驶
  • 相关阅读:
    162. 寻找峰值
    152. 乘积最大子数组
    MAT 下载及安装
    Netty NIO基础
    MySQL insert into V.S. replace into
    Netty NIO 之 阻塞模式/非阻塞模式/多路复用
    Python 函数参数*args和**kwargs
    Netty NIO之ByteBuffer
    Python numpy 和 pandas
    Financial 协方差 Covariance
  • 原文地址:https://www.cnblogs.com/wujianming-110117/p/14424081.html
Copyright © 2020-2023  润新知