# 論文閱讀 : SmoothQuant ## 主要重點整理 #### 背景與挑戰 - 大型語言模型(LLMs)雖然效能優異,但**運算與記憶體資源消耗巨大**。 - 量化是降低成本與加速推理的常用技術。 - 現有方法難以在**維持準確率**與**硬體效率**之間取得平衡。 **主要貢獻:提出 SmoothQuant 方法** * **類型**:Post-training Quantization(PTQ),不需再訓練。 * **特點**: * **訓練後量化**,無需額外微調。 * 同時對 **權重(weights)與啟動值(activations)進行 INT8 量化(W8A8)**。 * 適用於各類 LLM,包括:OPT、BLOOM、GLM、MT-NLG、LLaMA 1/2、Falcon、Mistral、Mixtral。 * **核心技術**: * **觀察**:權重較容易量化,啟動值較難。 * **創新點**:透過**數學等價轉換**,將啟動值的量化困難度「遷移」到權重,達到平滑 activation 的效果。 * 效能 * 實現最多 **1.56× 推理加速**與 **2× 記憶體縮減**。 * 幾乎**不影響模型準確率**。 * 支援單節點推理 530B 模型,**大幅降低 LLM 部署門檻與成本**。 ## Introduction * LLMs(如 GPT-3)具有卓越效能,但**推論成本與延遲極高**: * 例如 GPT-3(175B 參數)需 **至少 350GB 記憶體**(FP16),需 8×A6000 或 5×A100 才能執行推理。 * 高記憶體與計算需求會造成 **延遲過高**,無法滿足實際應用。 #### 解決方法:量化 * **使用低位元整數(如 INT8)對權重與啟動值量化**: * 降低記憶體用量、頻寬需求。 * 加速計算密集操作(如 GEMM、BMM)。 * INT8 通常可**節省 50% 記憶體,提升近 2× 吞吐**。 #### 挑戰 * 雖然 CNN 或小型 transformer(如 BERT)可以有效量化,但: * **大型 LLM 的 activation 含有大量 outliers**,難以量化。 * 尤其在模型規模 >6.7B 時,activation 中會出現極端值,導致準確率下降。 ### 現有方法比較 | 方法 | 概要說明 | 限制 | | -------------- | ------------------------------------------------- | ------------------------ | | **ZeroQuant** | 動態 per-token activation 量化 + group-wise weight 量化 | 無法維持如 OPT-175B 等超大模型的準確率 | | **LLM.int8()** | 混合精度(outlier 留在 FP16,其餘為 INT8) | 實作複雜,對硬體加速器不友善 | ### 提出方法:SmoothQuant ![image](https://hackmd.io/_uploads/BkT4kY2Ugg.png) #### 核心創新 * **觀察**:儘管 activation 難以量化,不同 token 在各 channel 間變化趨勢相似。 * **方法**: * 離線階段使用**通道級縮放轉換(per-channel scaling)**,將 activation 的極端值平滑處理。 * **將 activation 的量化難度「數學等價地遷移」給 weights**,使整體更易量化。 #### 相容性與效率 * SmoothQuant 是**通用、訓練後、無需額外微調**的 PTQ 方法。 * 相容多種量化方案,提供三種效率等級設定(O1–O3)。 #### 實驗與應用成效 * 支援多種大型模型(OPT-175B、BLOOM-176B、GLM-130B、MT-NLG-530B)。 * 在 PyTorch 上實測可達: * **最多 1.51× 推理速度提升** * **最多 1.96× 記憶體減少** * 整合進 FasterTransformer 框架: * 提升至 **1.56× speedup**、**記憶體減半** * 甚至能讓 **530B 模型在單一 8-GPU 節點中部署** ## Preliminaries ### 量化數學定義(整數型均勻量化(Integer Uniform Quantization)) $$ \bar{\mathbf{X}}^{\text{INT8}} = \left\lceil \frac{\mathbf{X^{\text{FP16}}}}{\Delta} \right\rfloor, \quad \Delta = \frac{\max(|\mathbf{X}|)}{2^{N-1}-1} $$ * $\mathbf{X}$:原始浮點張量。 * $\bar{\mathbf{X}}$:量化後的 INT8 張量。 * $\Delta$:量化 step size。 * $N$:位元數(本研究為 8 位元)。 * 假設為 **對稱量化**(symmetric, centered at 0);非對稱(asymmetric)量化則可加上 zero-point。 #### Step size 的估算方式 * **Static Quantization**:離線使用校準資料(calibration set)估算 activations 的 $\Delta$。 * **Dynamic Quantization**:於執行時(runtime)根據 activations 統計動態計算 $\Delta$。 ### 量化粒度(Granularity) ![image](https://hackmd.io/_uploads/SJTv4t3Ueg.png) | 類型 | 說明 | | --------------- | -------------------------------------------- | | **Per-tensor** | 全張量使用一個單一 step size,實作簡單,但誤差大。 | | **Per-channel** | 每個輸出通道用不同 step size,適用於 weight。 | | **Per-token** | 每個 token 的 activation 使用不同 step size,精細但開銷大。 | | **Group-wise** | 以 channel 群組為單位,為 per-channel 的粗略版本。 | ### 選擇 W8A8(Weights + Activations 都 INT8)? * 在 Transformer 中,典型的運算為: $$ \mathbf{Y} = \mathbf{X} \cdot \mathbf{W} $$ 其中 $\mathbf{X}$ 為 activation、$\mathbf{W}$ 為 weight。 * 只量化權重(W8)雖可節省空間,但不能用 **INT8 kernel** 加速。 * 為達成高效率推理,**需要 W8A8,才能使用硬體加速器上的 INT8 GEMM kernel**(如 NVIDIA GPU、Intel CPU 等支援)。 ### 問題核心:LLM 的 Activation 難以量化 #### 關鍵觀察與挑戰 1. **Activations 比 Weights 更難量化** * 權重的分布通常較平坦且容易近似。 * 過去研究已成功將 weights 量化為 INT8 / INT4,幾乎不影響準確率。 * Activation 則具有極端值(outliers),導致難以壓縮而不損失資訊。 2. Activation 中的 Outliers 對量化傷害極大 * 少數 outliers 可高達其他值的 **100 倍**,導致: * 計算 step size 時會被 outlier 主導 → 非 outlier 值的有效量化位數(bits)急劇下降。 * 比如 channel 中最大值為 $m_i$,整體最大為 $m$,則有效 bits 變成 $2^8 \cdot \frac{m_i}{m}$,有些 channel 實際只有 2–3 級別。 3. **Outliers 只出現在固定少數 Channels 且持續出現** * 若某 channel 有 outlier,會在所有 token 中持續存在。 * → channel 內部變異小,但不同 channel 間變異大。 ### 現有應對方式比較 | 量化策略 | 說明與效果 | 限制 | | ------------------------------------------------- | ------------------------------ | ----------------------------- | | **Per-tensor** | 全 tensor 一個 step size,最簡單 | 受 outlier 影響最大,誤差大 | | **Per-token** | 為每個 token 用不同 step size | 仍無法解決 outlier 集中在 channel 的問題 | | **Per-channel** | 每個 output channel 一個 step size | 效果好,但不相容硬體 | | **Simulated per-channel activation quantization** | 能接近 FP16 精度 | 僅限於模擬,實際不易部署 | #### Per-channel Activation 量化難部署 * 雖然 per-channel 最能降低 activation 的誤差,但: * 不符合現有硬體(如 NVIDIA Tensor Core)上 **INT8 GEMM 的執行路徑**。 * 這些硬體需要高速矩陣乘法序列,中間不能插入低速操作(如 rescale)。 * 可行的硬體友善量化方式: * 僅在矩陣乘法外部進行縮放(scaling): $$ \mathbf{Y} = \text{diag}(\Delta_X) \cdot (\bar{X}^{\text{INT8}} \cdot \bar{W}^{\text{INT8}}) \cdot \text{diag}(\Delta_W) $$ → 只允許在 token 維度(activations 的 $T$)或 output channel 維度(weights 的 $C\_o$)上做 scaling。 ### 小結 * **Activation 的 outliers 是 LLM 量化的主要瓶頸**,會造成低有效位數與精度下降。 * **Per-channel activation quantization 是理想方案,但不適用於現有硬體。** * 因此,現有方案(如 LLM.int8()、ZeroQuant)採用折衷的 per-token 量化,但仍無法有效解決根本問題。 * 本研究提出的 SmoothQuant 就是為了解決這個困境:**在硬體可接受的範圍內,達成類似 per-channel 效果的量化方式。** ## 論文核心方法論 ### **核心想法:轉換張量來遷移量化困難** ![image](https://hackmd.io/_uploads/ByTN1j38lx.png) ### **平滑 Activation、重配 Weight,保持等價性** SmoothQuant 的做法是: > **對 activation 每個 channel 做縮放(平滑),同時用反向比例縮放 weights,保持線性層運算等價性。** 數學上轉換為: $$ \mathbf{Y} = (\mathbf{X} \cdot \text{diag}(\mathbf{s})^{-1}) \cdot (\text{diag}(\mathbf{s}) \cdot \mathbf{W}) = \hat{\mathbf{X}} \cdot \hat{\mathbf{W}} $$ 其中: * $\mathbf{s}$:每個 channel 的「平滑因子 smoothing factor」 * $\hat{\mathbf{X}}$:平滑過的 activation,更適合量化 * $\hat{\mathbf{W}}$:重配過的 weight **優點** * 可將 activation 中的 outlier 平滑掉,讓 quantization bits 有效利用 * 運算仍與原模型相同(數學等價) ### **s 的選擇會決定 quantization 的誤差分佈** 若單純選擇: * $\mathbf{s}_j = \max(|\mathbf{X}_j|)$:將所有難度丟給 weights(會讓 weights 難量化) * $\mathbf{s}_j = 1 / \max(|\mathbf{W}_j|)$:則 activation 會難量化 **最佳策略是「均分難度」** ### **提出平衡控制因子 α(migration strength)** ![image](https://hackmd.io/_uploads/Byluyj38eg.png) $$ \mathbf{s}_j = \frac{\max(|\mathbf{X}_j|)^{\alpha}}{\max(|\mathbf{W}_j|)^{1-\alpha}} $$ * $\alpha \in [0, 1]$ 控制平衡比例 * $\alpha = 0.5$:activation 和 weights 各承擔一半 * $\alpha > 0.5$:activation outlier 多 → 多轉移給 weights * 實驗結果: * **OPT / BLOOM**:用 $\alpha = 0.5$ 效果最佳 * **GLM-130B**:outlier 多,$\alpha = 0.75$ 效果較好 效果是讓 activation 和 weight 在量化前具有「相似的最大值」,使 quantizer bits 被平均分配,量化誤差減少。 ### 實作方式與優化 * smoothing factor 可**離線提前融合到前一層的線性運算中**(如 Linear, LayerNorm): * 不增加額外 kernel call → 無推理效能損失 * 若遇到 residual 結構,也可加上殘差支路的額外縮放 ### 應用在 Transformer 上 ![image](https://hackmd.io/_uploads/SkIxei2Leg.png) > 將 W8A8 應用於所有 compute-heavy operator(Linear / BMM),其餘仍保持 FP16: | 模組類型 | 處理方式 | | ------------------- | ------- | | Linear / BMM | W8A8 量化 | | Softmax / LayerNorm | 保持 FP16 | | 激活函數(ReLU 等) | 保持 FP16 | ## Experiments ### Baseline 方法比較 | Baseline 方法 | 說明 | | ------------------- | ------------------------------------------------- | | W8A8 Naive | 權重與 activation 都簡單地量化為 INT8,無特殊處理 | | ZeroQuant | 動態 per-token activation 量化 + group-wise weight 量化 | | LLM.int8() | 混合精度策略:對 outlier 留 FP16,其餘用 INT8 | | Outlier Suppression | 用縮放方式降低 activation 中的極端值,改善量化精度 | 此外,SmoothQuant 提供三種效率 / 準確度等級(O1 \~ O3),**可視為一種 orthogonal augmentation strategy**,可搭配多種量化策略使用。 ### **實驗模型與評估任務** | 模型 | 評估任務(Zero-shot) | | ----------- | --------------------------------------------------------------------- | | OPT | LAMBADA, HellaSwag, PIQA, WinoGrande, OpenBookQA, RTE, COPA, WikiText | | BLOOM | 同上 | | GLM-130B | LAMBADA, MMLU, MNLI, QNLI(因部分 benchmark 出現在訓練資料中) | | MT-NLG 530B | 用於展示 scalability,首次在單節點上部署超過 500B 的模型 | * OPT/BLOOM 使用 `lm-eval-harness` 工具進行自動化評測。 * GLM-130B 使用官方評測工具。 * 主要觀察的是 **量化前後的相對性能變化**,非絕對分數。 ### **Activation Smoothing(平滑設定)** | 模型族群 | 適合的 α 值(遷移強度) | 原因 | | ----------- | ------------- | --------------------------- | | OPT / BLOOM | α = 0.5 | activation 難度中等,平均分配最穩定 | | GLM-130B | α = 0.75 | activation outlier 多,需要更多平滑 | * 使用 **Pile validation set** 中隨機抽取的 512 筆樣本進行: * smoothing factor 的估算 * static quantization step 的校準(Calibration) * 校準一次後,應用於所有 downstream 任務,驗證其泛化性與 zero-shot 表現。 ### **實作與部署細節** | 框架 | 用途 | 實作方式 | | --------------------- | ------------- | ----------------------------------- | | PyTorch (Huggingface) | 概念驗證原型 | 自訂 INT8 Linear 與 BMM 模組 | | FasterTransformer | 高效能部署環境(如伺服器) | 使用 CUTLASS INT8 GEMM 核心取代 FP16 運算模組 | * 在兩個框架中,都替換原本的 FP16 模組為 INT8 實作版本。 * 重點:**不需額外訓練、能直接部署在 INT8 支援的硬體上,如 NVIDIA Tensor Cores** ### 實驗結果整理 ![image](https://hackmd.io/_uploads/Hkw9GonLxx.png) | 方法 | 表現 | | -------------------------------------- | ---------------------------------------- | | **SmoothQuant** | 在所有量化策略下均可 **匹配 FP16 精度** | | **LLM.int8()** | 也能保留精度,但**需要浮點表示 outliers**,導致延遲變高 | | W8A8 / ZeroQuant / Outlier Suppression | 幾乎失效,輸出接近隨機 → 證明 LLM 的 activation 很難直接量化 | **結論**:SmoothQuant 是唯一能在不犧牲精度、又不增加延遲的情況下成功量化 OPT-175B 的方法。 ### 不同模型的量化難度比較 ![image](https://hackmd.io/_uploads/rkiHFsh8le.png) | 模型 | 量化難度 | SmoothQuant 表現 | 備註 | | ---------- | ---- | ----------------------------- | -------------------------------- | | BLOOM-176B | 易 | O1/O2 完美匹配 FP16,O3 精度僅下降 0.8% | activation 分布較穩定 | | GLM-130B | 難 | O1 可匹配 FP16,O3 下降 1%,仍大勝其他方法 | 遇到更多 outliers,採用 top-2% clipping | ### 各種模型規模都穩定 * OPT 系列(從小到大)皆經過測試 * 圖表 7 結果顯示: * **SmoothQuant 在所有規模下都能達到與 FP16 幾乎一致的準確率** * 適用於從 1.3B 到 175B 等級的 LLM ### **Instruction-Tuned LLM** ![image](https://hackmd.io/_uploads/HkeRFonIxx.png) * 測試對象:**OPT-IML-30B** * 資料集:WikiText-2、LAMBADA * SmoothQuant 表現: * 成功保持精度,W8A8 量化可穩定使用 * 備註: * 雖然是 instruction-tuned,但架構與 pretraining 相近 → 方法仍然適用 ### **LLaMA 模型** ![image](https://hackmd.io/_uploads/BJEQ5ih8xx.png) * 實驗對象:LLaMA 1 系列 * 觀察: * LLaMA activation 的 outlier 現象 **比 OPT / BLOOM 輕微** * SmoothQuant 依然可以做到 **W8A8 量化,且幾乎無性能損失** ### **更多新模型:LLaMA-2, Falcon, Mistral, Mixtral** ![image](https://hackmd.io/_uploads/S1DO5jn8ex.png) 結論:SmoothQuant 是一個具 **架構廣泛適用性** 的量化方法,**連 MoE 模型都能保持效能** ### Speedup, Memory Saving ![image](https://hackmd.io/_uploads/Byq2ionUgg.png) #### PyTorch 實作(單 GPU,OPT-6.7B \~ OPT-30B) | 比較項目 | SmoothQuant-O3 vs. FP16 | | ---------------- | -------------------------------------------- | | Latency | 最多可達 **1.51× 加速**(在 OPT-30B、seq=256 時) | | 記憶體 | 幾乎 **節省一半的 GPU 記憶體用量** | | 對比 LLM.int8() | LLM.int8() 雖保留精度,但速度比 FP16 還慢,因其混合精度處理產生額外開銷 | #### FasterTransformer 實作(多 GPU 可平行) ![image](https://hackmd.io/_uploads/By6Xni3Uxl.png) | 模型 | SmoothQuant-O3 效果 | | -------------- | ------------------------------------------------------ | | OPT-13B / 30B | 單 GPU 加速最多可達 **1.56×** | | OPT-66B / 175B | 可使用**一半的 GPU 數量達到相同或更快的 latency**(例:OPT-175B 僅需 4 GPU) | | 記憶體使用 | 降低至原來的 **約 1/2**,利於在資源有限的伺服器上部署大型模型 | ### Decoding 階段效能(逐 token 預測) ![image](https://hackmd.io/_uploads/B1w9633Ilg.png) | 比較項目 | SmoothQuant vs. FP16 | | -------------------- | ----------------------- | | Per-token latency | 最多可減少 **1.42× latency** | | 記憶體 | 同樣可節省 **約一半的記憶體使用量** | ### Ablation Study ### Quantization Scheme 粒度對效能的影響 ![image](https://hackmd.io/_uploads/SkXgbT3Uee.png) | 粒度級別(O1 → O3) | 越粗 → latency 越低 | | -------------------- | ----------------------------------------- | | Dynamic Quantization | 有 runtime overhead(需要在線計算 scale) | | Static Quantization | **推薦**(速度更快,尤其適合部署) | | Overall 建議 | **使用 O3(per-tensor static)在可接受精度損失下效能最佳** | ### 遷移強度 α 的選擇(activation ↔ weight 難度平衡) ![image](https://hackmd.io/_uploads/rJ2AZThUxl.png) | α 值 | 量化難點 | 效果 | | ------------ | -------------- | ---------------- | | < 0.4 | activation 難量化 | 精度下降 | | > 0.6 | weight 難量化 | 精度下降 | | 0.4 \~ 0.6 | 平衡最適 | **最佳量化效果(誤差最小)** | ### 總結 | 維度 | SmoothQuant-O3 結果 | | ---------- | ----------------------------------------------- | | 推理加速 | 最多提升 **1.56×(FasterTransformer)**;比 LLM.int8 快 | | 記憶體使用 | **節省約 2× GPU 記憶體**,支援更大模型部署 | | 可擴展性 | **530B 模型首次實現單節點部署(8×A100)** | | 實作容易 | 完全 post-training;支援 PyTorch 與 FasterTransformer | | 精度維持 | 各種模型與任務下,精度 **接近 FP16 baseline**,遠優於其他 INT8 方法 | | 消融分析結果明確 | α 在 0.4–0.6 之間最穩定,O3 粒度最適合實務部署 | ## 結論 ### SmoothQuant 是一種高準確率、高效率的 **後訓練量化方法(Post-Training Quantization, PTQ)**,其核心優勢包括: ### 1. **同時量化權重與 activation(W8A8)** * 能針對 LLM 中 **所有矩陣乘法(GEMMs)** 進行 INT8 量化 * 精度幾乎與 FP16 相當(lossless) * 適用模型規模涵蓋 **小型到超大型(高達 530B 參數)** --- ### 2. **極大化部署效率** * 相較於以往混合精度(如 LLM.int8)的方法: * **推理速度提升高達 1.56×** * **記憶體使用量減半** * 支援主流部署框架: * PyTorch(研究原型) * FasterTransformer(生產環境) --- ### 3. **轉變 LLM 推理門檻** * 不需重新訓練(訓練後量化) * 不需修改 CUDA 核心或特殊硬體 * 可直接用現成 INT8 kernel 加速(如 CUTLASS GEMM) ## 附註 ### SmoothQuant 與 GPTQ 比較整理 | 面向 | GPTQ | SmoothQuant | | ----- | ------------------------------ | --------------------------------- | | 量化對象 | 只量化 weights(activation = FP16) | 同時量化 weights + activations(W8A8) | | 支援階段 | 只支援生成(batch=1) | 支援 context + decoding,支援 batching | | 記憶體節省 | 主要減少權重 loading | 還能減少 KV cache 記憶體 | | 精度保持 | 需精細調參與 retrain | Post-training、快速部署 | | 整合潛力 | 可與 SmoothQuant 結合為 W4A4 | 可搭 GPTQ 改良 weight 端量化 | ### Per-channel Activation 對每個 activation 的 channel(特徵維度)使用不同的 scale(Δ)。 #### 現代硬體的設計偏好「大批次、規則、整齊的計算」 * 像 NVIDIA GPU 上的 **Tensor Cores**、Intel CPU 上的 **AVX / VNNI** 都針對 **固定形狀的矩陣乘法(GEMM)** 做了高度優化。 * 它們偏好這種流程: ``` INT8 activation × INT8 weight → INT32 結果 → 統一 scale 還原回 FP16 / FP32 ``` * 這樣可以保證整個 kernel 連續、無條件分支、批次並行。 #### Per-channel 代表你必須「每個 channel 用不同 scale」,等於要插入額外操作: * 若 activation 是 per-channel scale,就不能直接把 activation 傳進 GEMM,你得「先對每一行(或列)做縮放」。 * 這表示你得執行: ``` dequant(scale_i * x_i) for each i ``` → 在硬體上需要額外的 **逐 channel 乘法(FMA)指令**,這無法與 GEMM 並行,**會造成瓶頸與效率損失**。 ### s 的選擇會決定 quantization 的誤差分佈 考慮一層 Linear Layer: $$ \mathbf{Y} = \mathbf{X} \cdot \mathbf{W} $$ 假設: * Activation $\mathbf{X} \in \mathbb{R}^{2 \times 2}$ * Weight $\mathbf{W} \in \mathbb{R}^{2 \times 2}$ #### Activation channel 1 的數值(有 outlier): ``` X[:, 1] = [1.0, 100.0] → max = 100 ``` #### Weight channel 1 的數值: ``` W[:, 1] = [0.2, 0.3] → max = 0.3 ``` #### 若使用 $s_j = \max(|X_j|) = 100$ * **平滑後的 activation**: $$ \hat{X} = X / 100 = [0.01, 1.0] $$ 非常平坦、容易量化(數值都落在 0~1) * **相應放大的 weight**(乘上 100): $$ \hat{W} = W \cdot 100 = [20, 30] $$ 權重變得超大,導致: * 權重的最大值 = 30,非 outlier channel 變小,**scale 會被 outlier 主導**。 * → INT8 量化會浪費 bits 在極大值上,其他值只能被擠壓在低 bit 範圍,導致量化誤差大。 結果:**activation 好量化,但 weights 變成難量化,準確率掉很多。** #### 若使用 $s_j = 1 / \max(|W_j|) = 1 / 0.3 \approx 3.33$ * **平滑後的 activation**: $$ \hat{X} = X / 3.33 = [0.3, 30] $$ outlier 還是很明顯(30),仍然主導最大值。 * **相應的 weight**: $$ \hat{W} = W \cdot 3.33 = [0.67, 1.0] $$ 權重仍在小範圍內,容易量化。 結果:**weights 好量化,但 activation 仍然有嚴重 outlier,難量化,仍導致精度損失。** #### 平衡兩邊 → 用 α 例如使用: $$ s_j = \frac{\max(|X_j|)^{0.5}}{\max(|W_j|)^{0.5}} $$ 延續上面例子: * $\max(|X_j|) = 100$, $\max(|W_j|) = 0.3$ 計算: $$ s_j = \frac{100^{0.5}}{0.3^{0.5}} = \frac{10}{\sqrt{0.3}} \approx 10 / 0.55 \approx 18.2 $$ * Activation 除以 18.2: $$ [1.0, 100.0] → [0.055, 5.49] $$ * Weight 乘以 18.2: $$ [0.2, 0.3] → [3.64, 5.46] $$ 結果:activation 與 weight 的最大值都在 5\~6 之間 → 都比較好量化,誤差小