【问题标题】:initial_sparsity parameter in sparsity.PolynomialDecay() TensorFlow 2.0 magnitude-based weight pruningsparsity.PolynomialDecay() TensorFlow 2.0 基于幅度的权重修剪中的 initial_sparsity 参数
【发布时间】:2020-01-31 14:32:52
【问题描述】:

我正在尝试教程TensorFlow 2.0 Magnitude-based weight pruning with Keras 遇到了参数initial_sparsity

import tensorflow_model_optimization as tfmot
from tensorflow_model_optimization.sparsity import keras as sparsity
import numpy as np

epochs = 12
num_train_samples = x_train.shape[0]
end_step = np.ceil(1.0 * num_train_samples / batch_size).astype(np.int32) * epochs
print('End step: ' + str(end_step))

pruning_params = {
      'pruning_schedule': sparsity.PolynomialDecay(initial_sparsity=0.50,
                                                   final_sparsity=0.90,
                                                   begin_step=2000,
                                                   end_step=end_step,
                                                   frequency=100)
}

教程说:

这里使用的参数意思是:

稀疏度 PolynomialDecay 用于整个训练过程。我们从稀疏度 50% 开始,逐步训练 模型达到 90% 的稀疏度。 X% 稀疏性意味着 X% 的权重 张量将被修剪掉。

我的问题是,你不应该从 0% 的 initial_sparsity 开始,然后剪掉 90% 的权重吗?

initial_sparsity 为 50% 是什么意思?这是否意味着开始修剪 50% 的权重,然后实现 90% 的稀疏修剪?

另外,对于tfmot.sparsity.keras.ConstantSparsity,API如下:

pruning_params_unpruned = {
    'pruning_schedule': sparsity.ConstantSparsity(
        target_sparsity=0.0, begin_step=0,
        end_step = 0, frequency=100
    )
}

初始化具有恒定稀疏度的修剪计划。

稀疏性应用于间隔 [begin_step, end_step] 每 频率步长。在每个适用的步骤中,稀疏度(%) 是恒定的。

这是否意味着如果一个神经网络模型已经处于 50% 的稀疏度水平,但 target_sparsity = 0.5 那么剪枝计划会:

  1. 不进行修剪,因为模型已经处于 50% 的修剪水平
  2. 它进一步修剪已经(50% 修剪)模型的 50% 权重

您可以在PolynomialDecayConstantSparsity 中了解它

谢谢

【问题讨论】:

    标签: python tensorflow neural-network


    【解决方案1】:

    所以我还发现关于权重修剪的 Tensorflow 文档非常稀疏,所以我花了一些时间在调试器上弄清楚一切是如何工作的。

    修剪计划的工作原理

    在最基本的层面上,修剪计划只是一个将步骤作为输入并产生稀疏百分比的函数。然后使用该稀疏值生成一个掩码,该掩码用于修剪绝对值小于由绝对值权重分布和稀疏百分比给出的 k - 1 值的权重。

    多项式衰减

    类定义:Github Link
    上面类定义中包含的 cmets 帮助我理解了 PolynomialDecay 调度器的工作原理。

    剪枝率从 initial_sparsity 开始快速增长,但随后 缓慢稳定到目标稀疏度。

    应用的函数是

    current_sparsity = final_sparsity + (initial_sparsity - final_sparsity) * (1 - (step - begin_step)/(end_step - begin_step)) ^ 指数

    通过上述等式,当step == begin_step 时,则current_sparsity = initial_sparsity。因此,权重将在begin_step 参数指定的步骤上被修剪为initial_sparsity

    我同意您的评估,因为您通常希望以低于 50% 的稀疏度开始修剪,但我没有任何已发表的研究可以引用来支持该主张。您可以在 PolynomialDecay 类定义中引用的 paper 中找到更多信息,尽管我自己没有机会阅读。

    恒定稀疏度

    类定义:Github Link
    这个调度器的目的似乎非常有限。对于每个有效的修剪步骤,都会返回 target_sparsity。因此,多个修剪步骤非常多余。此调度程序的用例似乎是在训练期间进行一次修剪。使用此调度程序进行多次修剪的能力是将其与其父抽象类和其他修剪调度程序对齐。

    创建自己的修剪调度程序

    如果上面的两个调度器没有让你的船浮起来,抽象类PruningSchedule 暴露了一个端点,这使得创建你自己的修剪调度器变得非常容易,尽管它可能很复杂。以下是我自己创建的示例。

    免责声明:这个调度器是一个 19 岁大学生的想象的创造,没有任何已发表的文献依据。

    PruningSchedule = tfmot.sparsity.keras.PruningSchedule
    
    class ExponentialPruning(PruningSchedule):
    def __init__(self, rate=0.01, begin_step=0, frequency=100, max_sparsity=0.9):
        self.rate = rate
        self.begin_step = begin_step
        self.frequency = frequency
        self.max_sparsity = max_sparsity
    
        # Validation functions provided by the parent class
        # The -1 parameter is for the end_step
        # as this pruning schedule does not have one
        # The last true value is a boolean flag which says it is okay
        # to have no end_step
        self._validate_step(self.begin_step, -1, self.frequency, True)
        self._validate_sparsity(self.max_sparsity, 'Max Sparsity')
    
    def __call__(self, step):
        # Sparsity calculation endpoint
    
        # step is a integer tensor
    
        # The sparsity returned by __call__ must be a tensor
        # of dtype=tf.float32, so tf.math is required.
    
        # In the logic below, you can assume that a valid
        # pruning step is passed.
    
        p = tf.math.divide(
            tf.cast(step - self.begin_step, tf.float32),
            tf.constant(self.frequency, dtype=tf.float32)
        )
        sparsity = tf.math.subtract(
            tf.constant(1, dtype=tf.float32),
            tf.math.pow(
                tf.constant(1 - self.rate, dtype=tf.float32),
                p
            )
        )
    
        sparsity = tf.cond(
            tf.math.greater(sparsity, tf.constant(self.max_sparsity, dtype=tf.float32)),
            lambda: tf.constant(self.max_sparsity, dtype=tf.float32),
            lambda: sparsity
        )
    
        # This function returns a tuple of length 2
        # The first value determines if pruning should occur on this step
        # I recommend using the parent class function below for this purpose
        # The negative one value denotes no end_step
        # The second value is the sparsity to prune to
        return (self._should_prune_in_step(step, self.begin_step, -1, self.frequency),
                sparsity)
    
    def get_config(self):
        # A function required by the parent class
        # return the class_name and the input parameters as
        # done below
        return {
            'class_name': self.__class__.__name__,
            'config': {
                'rate': self.rate,
                'begin_step': self.begin_step,
                'frequency': self.frequency,
                'max_sparsity': self.max_sparsity
            }
        }
    

    使用修剪调度程序

    如果您只想修剪某些层,而不是所有可修剪的层,您可以在要添加到模型的层上调用 prune_low_magnitude 函数。

    prune_low_magnitude = tfmot.sparsity.keras.prune_low_magnitude
    model = keras.models.Sequential()
    ...
    model.add(prune_low_magnitude(keras.layers.Dense(8, activation='relu', kernel_regularizer=keras.regularizers.l1(0.0001)),
      ExponentialPruning(rate=1/8)))
    

    还要确保将UpdatePruningStep 实例传递给训练回调:

    m.fit(train_input, train_labels, epochs=epochs, validation_data=[test_input, test_labels],
      callbacks=[UpdatePruningStep()])
    

    【讨论】:

    • 我在 numpy 中创建了自己的自定义修剪算法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-21
    相关资源
    最近更新 更多