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: 量化类型,“symmetric”(对称)或“asymmetric”(非对称),默认为“symmetric”(对称)。

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 个 gas 边界进行特征值计算,默认值为 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
启用特征值

如我们在下表中看到,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) 它根据层的敏感度自动为每一层识别出良好的量化调度。

更新: