微调前量化还是微调后量化更好?

作者:

Jambay Kinley, Sam Kemp

2024年11月19日

👋 引言

机器学习中的量化是一种用于减少计算中使用数字精度的技术,这有助于提高模型的效率。量化不使用高精度浮点数(如 32 位或 16 位),而是将这些数字转换为较低精度的格式,例如 8 位整数。量化的主要好处是模型尺寸更小、计算速度更快,这对于在手机或嵌入式系统等资源受限的设备上部署模型特别有用。然而,精度的降低有时会导致模型准确率略有下降。

使用 LoRA(低秩自适应,Low-Rank Adaptation)方法微调 AI 模型是将大语言模型适应特定任务或领域的有效方法。LoRA 不会重新训练所有模型参数,而是通过冻结原始模型权重并将更改应用到单独的一组权重(然后将其添加到原始参数中)来修改微调过程。这种方法将模型参数转换为较低秩的维度,减少了需要训练的参数数量,从而加快了过程并降低了成本。

在对模型进行微调和量化时,确定正确的顺序非常重要

  • 微调量化还是微调后量化更好?

理论上,微调前进行量化应该能生成更好的模型,因为 LoRA 权重的训练所用的量化基础模型权重与部署时所用的相同。这避免了在使用浮点基础权重训练然后部署量化基础模型时发生的准确率损失。在这篇博文中,我们展示了 Olive(一个用于 ONNX 运行时的先进模型优化工具包)如何帮助您回答在给定模型架构和场景下何时量化以及使用哪种量化算法的问题。

此外,作为回答何时量化问题的一部分,我们将展示以下不同的量化算法如何影响准确率

  • 激活感知权重量化(AWQ,Activation-Aware Weight Quantization)是一种旨在优化大语言模型(LLM)以实现高效执行的技术。AWQ 通过考虑推理期间产生的激活来量化模型的权重。这意味着量化过程考虑了激活中的实际数据分布,与传统权重量化方法相比,能够更好地保持模型的准确率
  • 广义训练后量化(GPTQ,Generalized Post-Training Quantization)是一种专为生成式预训练转换器(GPT)模型设计的训练后量化技术。它将模型的权重量化为更低的位宽(例如 4 位整数),以减少内存使用和计算需求,同时不会显著影响模型的准确率。该技术独立量化权重矩阵的每一行,以找到使误差最小化的权重版本

⚗️ 使用 Olive 运行实验

为了回答关于量化和微调正确顺序的问题,我们利用了 Olive (ONNX Live) —— 一个先进的模型优化工具包,旨在简化针对 ONNX 运行时部署优化 AI 模型的流程。

注意:量化和微调都需要在配备 Nvidia A10 或 A100 GPU 的机器上运行。

1. 💾 安装 Olive

我们使用 pip 安装了 Olive CLI

pip install olive-ai[finetune]
pip install autoawq
pip install auto-gptq

2. 🗜️ 量化

我们使用以下 Olive 命令,分别通过 AWQ 和 GPTQ 算法对 Phi-3.5-mini-instruct 进行量化

# AWQ Quantization
olive quantize \
  --algorithm awq \
  --model_name_or_path microsoft/Phi-3.5-mini-instruct \
  --output_path models/phi-awq

# GPTQ Quantization
olive quantize \
  --algorithm gptq \
  --model_name_or_path microsoft/Phi-3.5-mini-instruct \
  --data_name wikitext \
  --subset wikitext-2-raw-v1 \
  --split train \
  --max_samples 128 \
  --output_path models/phi-gptq 

3. 🎚️ 微调

我们使用 Hugging Face 上的 tiny codes 数据集微调量化模型。这是一个受限数据集,你需要申请访问权限。一旦获得访问权限,你应该使用你的访问令牌(access token)登录 Hugging Face

huggingface-clu login --token TOKEN

Olive 可以使用以下命令进行微调

# Finetune AWQ model
olive finetune \
  --model_name_or_path models/phi-awq \
  --data_name nampdn-ai/tiny-codes \
  --train_split "train[:4096]" \
  --eval_split "train[4096:4224]" \
  --text_template "### Language: {programming_language} \n### Question: {prompt} \n### Answer: {response}" \
  --per_device_train_batch_size 16 \
  --per_device_eval_batch_size 16 \
  --max_steps 100 \
  --logging_steps 25 \
  --output_path models/phi-awq-ft

# Finetune GPTQ model
olive finetune \
  --model_name_or_path models/phi-gptq \
  --data_name nampdn-ai/tiny-codes \
  --train_split "train[:4096]" \
  --eval_split "train[4096:4224]" \
  --text_template "### Language: {programming_language} \n### Question: {prompt} \n### Answer: {response}" \
  --per_device_train_batch_size 16 \
  --per_device_eval_batch_size 16 \
  --max_steps 100 \
  --logging_steps 25 \
  --output_path models/phi-gptq-ft

注意:我们还做了相反的顺序,即先微调然后运行量化。命令是相同的,只是顺序不同。

4. 🎯 运行困惑度(Perplexity)测试

我们使用 Olive 对模型运行了 困惑度指标。首先,我们在名为 perplexity-config.yaml 的文件中定义了以下 Olive 配置,该配置使用了 Olive 的评估功能

input_model:
  type: HfModel
  model_path: models/phi-awq-ft/model
  adapter_path: models/phi-awq-ft/adapter
systems:
  local_system:
    type: LocalSystem
    accelerators:
      - device: gpu
        execution_providers:
          - CUDAExecutionProvider
data_configs:
  - name: tinycodes_ppl
    type: HuggingfaceContainer
    load_dataset_config:
      data_name: nampdn-ai/tiny-codes
      split: 'train[5000:6000]'
    pre_process_data_config:
      text_template: |-
        ### Language: {programming_language}
        ### Question: {prompt}
        ### Answer: {response}
      strategy: line-by-line
      max_seq_len: 1024
    dataloader_config:
      batch_size: 8
evaluators:
  common_evaluator:
    metrics:
      - name: tinycodes_ppl
        type: accuracy
        sub_types:
          - name: perplexity
        data_config: tinycodes_ppl
passes: {}
auto_optimizer_config:
  disable_auto_optimizer: true
evaluator: common_evaluator
host: local_system
target: local_system
output_dir: models/eval

注意:我们为其他模型定义了相同的配置,但更新了 input_model

然后我们使用以下命令执行了 Olive 配置

olive run --config perplexity-config.yaml

📊 结果

Phi-3.5-Mini-Instruct

下表显示了以下各项的困惑度指标

  1. 不同量化和微调顺序(洋红色)
  2. Phi-3.5-Mini-Instruct 基础模型(虚线绿色),未量化
  3. Phi-3.5-Mini-Instruct 微调模型(实线绿色),未量化
Perplexity metrics for Phi-3.5

目标是让量化模型尽可能接近微调模型(实线绿色)。以下有几个要点

  • 量化对模型质量没有显著影响——从量化模型的困惑度分数与微调基础模型的接近程度可以看出这一点。
  • 微调量化确实比微调后量化能产生更好的结果。
  • 在这种情况下,GPTQ 提供的准确率比 AWQ 更好。

Llama-3.1-8B-Instruct

下表显示了以下各项的困惑度指标

  1. 不同量化和微调顺序(蓝色)
  2. Llama-3.1-8B-Instruct 基础模型(虚线绿色),未量化
  3. Llama-3.1-8B-Instruct 微调模型(实线绿色),未量化
Perplexity metrics for Llama-3.1-8B-Instruct

目标是让量化模型尽可能接近微调模型(实线绿色)。以下有几个要点

  • 量化对模型质量没有显著影响——从量化模型的困惑度分数与微调基础模型的接近程度可以看出这一点。
  • 微调量化确实比微调后量化能产生更好的结果。
  • GPTQ and AWQ give similar model quality results.

结论

在这篇博文中,我们演示了如何利用 Olive 来解决常见的 AI 模型优化问题。我们的研究结果表明,在微调前进行量化可以提高 Phi-3.5-mini-instruct 和 Llama-3.1-8B-Instruct 的模型质量。这些量化变体在质量上与其全精度 (FP32) 对应版本非常接近,同时需要更少的内存和存储空间。这凸显了端侧 AI(on-device AI)在以更低资源占用提供高质量性能方面的潜力。