一、量化

1.1、目的

        大模型量化的主要目的是壓縮模型參數,降低計算複雜度,提高推理效率。

        存儲優化:將高精度的浮點型參數轉換為低精度的整數參數,減少模型存儲空間。

        計算加速:使用低精度整數進行計算,降低計算複雜度,提高推理速度。

        硬件適配:某些硬件(如嵌入式設備、移動端芯片)對低精度計算支持更好,量化可以提高 適配性。

精度

存儲位數

結構

Float32 (FP32)

32 位

1位符號位,8位指數位,23位尾數位

Float16 (FP16)

16 位

1位符號位,5位指數位,10位尾數位

Bfloat16 (BF16)

16 位

1位符號位,8位指數位,7位尾數位

Int8

8 位

僅整數,無指數部分

Int4

4 位

僅整數,無指數部分

舉例:7B(70億參數)的大模型在不同精度下的存儲大小:

  • FP16:約 14GB
  • INT8:約 8GB
  • INT4:約 4GB

1.2、量化分類

量化方法可以根據是否使用額外數據進行校準,分為兩大類:

        在線量化 (On Quantization):使用額外的數據集進行量化,主要目的是減少精度損失, 保證模型效果。

        離線量化 (Off Quantization):直接對已有的模型進行量化,可能使用少量或不使用額外 數據。

1.3、量化的優缺點

類別

優點

侷限性

減少存儲

顯著減少模型權重的存儲空間。

-

推理速度

降低計算開銷,適用於移動設備和雲端推理。

-

節約能耗

降低計算功耗,使模型適用於嵌入式設備。

-

精度損失

-

可能導致模型推理精度下降,尤其是大規模 LLM。

訓練開銷

-

某些量化方法需要額外的訓練或微調。

硬件支持

-

低精度計算(如 INT4、NF4)需要特定硬件支持。

1.4、量化的應用場景 

邊緣計算:在移動設備、智能攝像頭上運行高效 AI 模型。

服務器部署:減少 GPU/TPU 顯存佔用,提高吞吐量。

模型壓縮:降低存儲需求,便於模型分發。

1.5、量化計算方式

Python量化交易入門_黑馬程序員的技術博客_數位

Python量化交易入門_黑馬程序員的技術博客_權重_02

        反量化(Dequantization)主要用於將量化後的整數值恢復為浮點數,以便進行更精確的計 算,尤其是在推理過程中。 

        在大模型推理時,量化的參數(如 INT8 或 INT4)用於計算,以提高推理效率。但最終模型的 某些計算(如 Softmax、LayerNorm)仍然需要浮點精度,因此需要在適當的地方進行反量化。

import torch

float_tensor = torch.tensor([2.5, -3.1, 7.6, -1.2, 5.8], dtype=torch.float32)
# x_int = round(x_float / scale + zero_point)
quantized_tensor = torch.quantize_per_tensor(float_tensor, scale=0.1, zero_point=0, dtype=torch.qint8)

print("Float32:", float_tensor)
print("Int8:", quantized_tensor.int_repr())

# Float32: tensor([ 2.5000, -3.1000,  7.6000, -1.2000,  5.8000])
# Int8: tensor([ 25, -31,  76, -12,  58], dtype=torch.int8)

        注意: 離羣值(outliers)可能會影響 scale,導致大部分數據的量化誤差增加。 解決方案包括截斷離羣值,或者使用更復雜的分組量化方法。

import torch
import torch.nn as nn
import os

# 創建 FP16 模型
fp16_model = nn.Sequential(
    nn.Linear(1280, 1280),
    nn.Linear(1280, 1280)
).to(torch.float16).to(0)

# 保存 FP16 模型
torch.save(fp16_model.state_dict(), "model_fp16.pt")

# 進行動態量化
quantized_model = torch.quantization.quantize_dynamic(
    fp16_model,  # 原始模型
    {nn.Linear},  # 需要量化的層
    dtype=torch.qint8  # 量化為 INT8
)

# 保存 INT8 量化模型
torch.save(quantized_model.state_dict(), "model_int8.pt")

# 計算文件大小
size_fp16 = os.path.getsize("model_fp16.pt")
size_int8 = os.path.getsize("model_int8.pt")

print("Model: FP16\tSize (KB):", size_fp16 / 1e3)
print("Model: INT8\tSize (KB):", size_int8 / 1e3)
print("Compression Ratio: {:.2f}x".format(size_fp16 / size_int8))
'''
Model: FP16     Size (KB): 6560.872
Model: INT8     Size (KB): 3285.412
Compression Ratio: 2.00x
'''

 二、GPTQ量化

        GPTQ:ACCURATE POST-TRAINING QUANTIZATION FOR GENERATIVE PRE-TRAINED TRANSFORMERS(用於生成式預訓練變換器的高精度訓練後量化)

GPTQ :[2210.17323] GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers

        GPTQ (GPT Quantization) 是一種訓練後量化 (Post-Training Quantization, PTQ) 方法,旨 在將預訓練的 Transformer 模型權重精確地量化到低精度,而無需額外的訓練數據。GPTQ 的 核心思想來源於 Optimal Brain Damage (OBD) 及其後續的優化方法。即其核心思想源自 Optimal Brain Damage (OBD),隨後經過 OBS、 OBQ等優化,最終形成 GPTQ。 

OBD (剪枝) -> OBS (剪枝) -> OBQ (量化) -> GPTQ (量化)。

Python量化交易入門_黑馬程序員的技術博客_權重_03

逐層獨立的量化(Layer-wise Independent Quantization)

        GPTQ 不是一次性量化整個模型,而是逐層進行。 

貪婪量化(Greedy Quantization based on Loss Minimization)

        GPTQ 採用 逐個權重量化 的方式,而不是同時量化所有權重。 

分組量化(Group-wise Quantization)

        GPTQ 不是一個權重一個權重地量化,而是按小組(group)進行量化 

int4 / fp16 混合精度量化 (W4A16) 

        GPTQ 採用了一種 混合精度(Mixed Precision)策略: 

組件

精度

説明

權重(Weights)

int4 (4-bit 整數)

體積減少 8 倍,節省存儲和計算。

激活值(Activations)

fp16 (16-bit 浮點數)

保持一定的計算精度,避免信息丟失。

        GPTQ 加速了 OBQ,減少計算複雜度,使 Transformer 量化可行,兼顧效率和精度,成為高效 的 LLM 量化方案。 

# 安裝 auto-gptq 庫,用於執行 GPTQ 量化
# pip install auto-gptq

# 升級 accelerate, optimum 和 transformers 庫,確保使用最新版本以獲得最佳兼容性和功能
# pip install --upgrade accelerate optimum transformers

# 從 transformers 庫導入必要的類
from transformers import AutoModelForCausalLM, AutoTokenizer, GPTQConfig

# 指定要量化的預訓練模型的 ID (這裏是 Facebook 的 OPT-125m 模型)
model_id = "facebook/opt-125m"

# 使用 AutoTokenizer 加載與指定模型 ID 對應的 tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_id)

# 配置 GPTQ 量化的參數
gptq_config = GPTQConfig(
    bits=4,             # 將模型權重進行 4 比特量化
    dataset="c4",       # 使用 "c4" 數據集進行量化校準
    tokenizer=tokenizer # 將加載的 tokenizer 傳遞給 GPTQ 配置
) # GPTQ 量化 參數

# 使用 AutoModelForCausalLM 加載預訓練模型,並應用 GPTQ 量化
quantized_model = AutoModelForCausalLM.from_pretrained(
    model_id,
    device_map="auto",          # 自動將模型分配到可用的設備 (GPU 或 CPU)
    quantization_config=gptq_config # 應用之前定義的 GPTQ 配置
) # 進行 量化 操作

# 保存量化後的模型到本地目錄 "opt-125m-gptq"
quantized_model.save_pretrained("opt-125m-gptq") # 保存 量化 後 的 模型

# 保存模型的 tokenizer 到相同的本地目錄
tokenizer.save_pretrained("opt-125m-gptq") # 保存 模型 字典 文件

三、AWQ量化

        AWQ:Activation-aware Weight Quantization for LLM Compression and Acceleration(用於 LLM 壓縮和加速的激活感知權重的量化)。

原論文地址:https://arxiv.org/pdf/2306.00978。

源碼:https://github.com/mit-han-lab/llm-awq。

        AWQ發現,模型的權重並不同等重要,權重中有一小部分突出的權重(salient weights)對模型精度至關重要。只要跳過這一部分權重的量化,就可以很大程度上提高量化後的模型精度。作者發現僅保留0.1%-1%的權重通道為FP16格式,可以顯著提高量化模型的性能。這表明基於激活分佈選擇關鍵權重是有效的。

所以如何來找到這0.1%-1%的權重通道是至關重要的:

Python量化交易入門_黑馬程序員的技術博客_數位_04

 

Python量化交易入門_黑馬程序員的技術博客_#python_05

        在FP16中保持較小的權重分數(0.1%-1%),在四捨五入到最近(RTN)上顯著提高了量化模型的性能。只有當我們通過觀察激活分佈而不是權重分佈來選擇 FP16 中的重要權重時,它才有效。我們用128的組大小使用INT3量化並測量了維基文本困惑度(↓)。 

        因此,基於激活值分佈挑選顯著權重是最為合理的方式。只要把這部分權重保持FP16精度,對其他權重進行低比特量化,就可以在保持精度幾乎不變的情況下,大幅降低模型內存佔用,並提升推理速度。

        為了避免方法在實現上過於複雜,在挑選顯著權重時,並非在“元素”級別進行挑選,而是在“通道(channel)”級別進行挑選,即權重矩陣的一行作為一個單位(attention是linear層)。在計算時,首先將激活值對每一列求絕對值的平均值,然後把平均值較大的一列對應的通道視作顯著通道,保留FP16精度。對其他通道進行低比特量化,如下圖:

Python量化交易入門_黑馬程序員的技術博客_數位_06

        我們觀察到,我們可以在LLMs基於激活分佈(中)中找到1%的顯著權重。保持FP16中的顯著權重可以顯著提高量化性能(PPL從43.2(左)到13.0(中)),但混合精度格式的硬件效率不高。我們遵循激活意識原則並提出AWQ(右)。AWQ執行每通道縮放頂保護突出權重和減少量化誤差。我們測量OPT-6.7BunderINT3-g128量化的困惑度