是微调之前量化还是之后?

作者

Jambay Kinley, Sam Kemp

2024年11月19日

👋 引言

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

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

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

  • 是微调 之前 量化还是之后?

理论上,在微调之前进行量化应该会产生更好的模型,因为 LoRA 权重是使用将要部署的相同量化基础模型权重进行训练的。这避免了在浮点(float)基础权重上训练然后使用量化基础模型部署时发生的准确性损失。在这篇博客文章中,我们将演示 Olive(ONNX Runtime 的最先进的模型优化工具包)如何帮助您确定何时进行量化以及针对给定的模型架构和场景使用哪种量化算法。

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

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

⚗️ 使用 Olive 运行实验

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

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

1. 💾 安装 Olive

我们使用 pip 安装了Olive CLI

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

2. 🗜️ 量化

我们使用 AWQ 和 GPTQ 两种算法以及以下 Olive 命令对 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 数据集对 量化后的模型 进行微调。这是一个受限数据集,您需要申请访问权限。获得访问权限后,您应该使用您的访问令牌登录 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. 🎯 运行困惑度评估

我们使用 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 和 AWQ 在模型质量上给出了相似的结果。

结论

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