DeepSpeed混合量化(MoQ)

DeepSpeed引入了对使用量化进行模型压缩的新支持,称为混合量化(MoQ)。MoQ是在QAT(量化感知训练)之上设计的,区别在于它在训练过程中安排了各种数据精度。它从使用高精度(例如FP16或16位量化)对模型进行量化开始,并通过预定义的调度程序降低精度,直到达到目标量化位数(例如8位)。此外,我们使用模型参数的二阶信息来分别动态调整网络每一层的量化调度。我们已经看到,通过添加这样的调度程序并在训练过程中使用各种数据精度,我们可以更高质量地量化模型并保留准确性。为了更好地理解MoQ方法,请参阅MoQ深入解读,此处

下面,我们使用GLUE任务的微调来说明如何使用MoQ。

先决条件

要将MoQ用于模型量化训练,您应该满足以下两个要求

  1. 使用入门指南将DeepSpeed集成到您的训练脚本中。
  2. 添加参数以配置您的模型,我们将在下面定义MoQ参数。

MoQ量化调度由许多参数定义,允许用户探索不同的配置。

MoQ参数

enabled:是否启用量化训练,默认为False。

quantize_verbose:是否显示详细细节,默认为False。

quantizer_kernel:是否启用量化内核,默认为False。

quantize_type:量化类型,“对称”或“非对称”,默认为“对称”。

quantize_groups:量化组,显示用于量化模型的比例数,默认为1。

quantize_bits,用于控制数据精度从起始位到最终目标位(例如,从16位降到8位)的位数。

`start_bits`: The start bits in quantization training. Default is set to 16.
`target_bits`: The target bits in quantization training. Default is set to 16.

quantize_schedule,这决定了如何在每个精度级别安排训练步骤。

`quantize_period`: indicates the period by which we reduce down the precision (number of bits) for quantization. By default, we use a period of 100 training steps, that will be doubled every time the precision reduces by 1 bit.
`schedule_offset`: indicates when the quantization starts to happen (before this offset, we just use the normal training precision which can be either FP32/FP16). Default is set to 100 steps.

quantize_algo,用于量化模型的算法。

`q_type`: we currently support symmetric and asymmetric quantization that result in signed and unsigned integer values, respectively. Default is set to symmetric
`rounding`: for the rounding of the quantized values, we can either round to the nearest value or use stochastic rounding. Default is set to nearest.

特征值参数

enabled:是否启用使用特征值调度的量化训练,默认值为False。

verbose:是否显示特征值计算的详细细节,默认值为False。

max_iter:计算特征值时的最大迭代次数,默认值为100。

tol:计算特征值时的容差误差,默认值为1e-2。

stability:方差稳定因子,默认值为1e-6。

gas_boundary_resolution:指示每N个气体边界计算特征值,默认值为1。

layer_name:指向所有用于特征值计算的层的模型范围名称,默认值为“bert.encoder.layer”。

layer_num:计算特征值的层数。

如何将MoQ用于GLUE训练任务

在使用DeepSpeed MoQ微调GLUE任务之前,您需要

  1. 安装DeepSpeed。
  2. 检出Huggingface transformers分支,并使用所有必需的软件包进行安装。

DeepSpeed配置文件

准备一个如下所示的配置文件test.json,请注意以下量化训练的重要参数

{
    "optimizer": {
      "type": "AdamW",
      "params": {
        "lr": 2e-5,
        "weight_decay": 0.0,
        "bias_correction": true
      }
    },
    "gradient_clipping": 1.0,
    "fp16": {
      "initial_scale_power": 16,
      "enabled": true
    },
    "quantize_training": {
      "enabled": true,
      "quantize_verbose": true,
      "quantizer_kernel": true,
      "quantize-algo": {
        "q_type": "symmetric"
      },
      "quantize_bits": {
        "start_bits": 16,
        "target_bits": 8
      },
      "quantize_schedule": {
        "quantize_period": 400,
        "schedule_offset": 0
      },
      "quantize_groups": 8,
    }
}

测试脚本

huggingface/examples文件夹下创建一个脚本文件,如下所示,使用上面准备的json文件启用DeepSpeed。

这里我们以MRPC任务为例。

TSK=mrpc
TEST_JSON=test.json

python text-classification/run_glue.py \
  --model_name_or_path bert-base-cased \
  --task_name $TSK \
  --do_train \
  --do_eval \
  --max_seq_length 128 \
  --per_device_train_batch_size 32 \
  --learning_rate 2e-5 \
  --num_train_epochs 3 \
  --output_dir /tmp/$TSK/ \
  --fp16 \
  --warmup_steps 2 \
  --deepspeed test.json

运行此脚本将获得使用MoQ量化的MRPC准确性和F1度量结果。

使用二阶信息(特征值)进行动态调度量化

特征值可用作训练期间层敏感性的代理,可用于创建逐层量化调度。启用特征值计算后,DeepSpeed将在gas_boundary_resolution处计算每个指定层的特征值,并使用它将quantize_period最多增加5倍,具体取决于层敏感性,以允许层在下一个精度降低阶段之前有足够的迭代次数进行适应。5倍的因子是根据启发式方法选择的。

请注意

  1. 启用特征值将使训练速度大大降低,它需要更长的时间来计算每一层的特征值。
  2. 在fp16训练期间,某些层的某些特征值可能会由于范围有限而变成NaN/Inf。对于这些层,我们返回所有层中所有非NaN/Inf特征值的最大值。如果所有特征值都是NaN,我们为每个特征值返回1.0。
  3. 特征值可以将quantize_period最多增加5倍(基于启发式方法选择)。当与每个1位精度降低阶段期间quantize_period加倍相结合时,这可能导致非常大的quantize_period,尤其是在初始quantize_period很大时。因此,在使用特征值时,务必从相对较小的quantize_period开始,以允许训练在训练结束之前经历所有精度转换阶段。
  4. 启用特征值并不能保证获得更好的准确性结果,通常需要与其他设置(例如start_bitsquantize_periodquantize_groups)一起进行调整。
{
	......

    "quantize_training": {
      "enabled": true,
      "quantize_verbose": true,
      "quantizer_kernel": true,
      "quantize_type": "symmetric",
      "quantize_bits": {
        "start_bits": 12,
        "target_bits": 8
      },
      "quantize_schedule": {
        "quantize_period": 10,
        "schedule_offset": 0
      },
      "quantize_groups": 8,
      "fp16_mixed_quantize": {
        "enabled": false,
        "quantize_change_ratio": 0.001
      },
      "eigenvalue": {
        "enabled": true,
        "verbose": true,
        "max_iter": 50,
        "tol": 1e-2,
        "stability": 0,
        "gas_boundary_resolution": 1,
        "layer_name": "bert.encoder.layer",
        "layer_num": 12
      }
    }
}

微调结果

在这里,我们展示了使用量化进行GLUE任务微调的结果。下表说明了我们用于每个任务以达到报告的准确性的调度参数。对于所有这些实验,我们使用具有8组的对称分组量化。

任务 STSB MRPC COLA WNLI SST2 RTE QNLI QQP MNLI
起始位数 12 12 12 12 12 12 12 12 14
周期 10 10 8 8 400 8 64 18 12
启用特征值 False True True True False True False True True

正如我们在下表中看到的,MoQ在不同的下游任务中始终保持准确性。

任务 STSB MRPC COLA WNLI SST2 RTE QNLI QQP MNLI SQuAD ACC+
无QAT(FP16) 88.71 88.12 56.78 56.34 91.74 65.3 90.96 90.67 84.04 90.56 0
基本QAT 88.9 88.35 52.78 55.3 91.5 64.2 90.92 90.59 84.01 90.39 -0.87
MoQ 88.93 89 59.33 56.34 92.09 67.15 90.63 90.94 84.55 90.71 0.75

提示

使用MoQ时,需要在设置正确的量化周期或偏移量之前考虑样本数量和训练迭代次数,以确保量化在训练结束前达到所需的精度级别。

为量化启用特征值会动态调整网络不同部分的量化周期。这有两个积极的影响:1)量化网络可能比使用相同的quantize_period量化每一层产生更高的准确性;2)它根据每一层的敏感性自动识别良好的量化调度。

更新: