# 論文閱讀 : SparseGPT ## 摘要 本論文首次證明,**大型 GPT 類生成模型可以在不進行任何再訓練的情況下,一次性(one-shot)剪枝達到至少 50% 稀疏度**,且準確率幾乎不受影響。 ### 方法 - 專為大型 GPT 模型設計,在效率與準確性之間取得良好平衡。 - 能應用於目前最大的開源 GPT 模型,如: - OPT-175B - BLOOM-176B - 效能 - 在 OPT-175B 和 BLOOM-176B 上執行 SparseGPT 不到 4.5 小時即可完成。 - 可以達到 60% 非結構化剪枝(unstructured sparsity),困惑度(perplexity)幾乎不增加。 - 可以忽略超過 1000 億個參數,無需 retraining,推理速度卻維持效能。 - 支援半結構化剪枝模式,如 2:4、4:8。 - 可以與現有的 權重量化方法結合使用,例如 INT8、INT4。 ## Introduction ### LLMs 難以部署的挑戰 * GPT 系列雖然在多種任務中表現出色,但其龐大的 **參數數量與推理資源消耗**,成為實際部署的重大障礙。 * 以 GPT-175B 為例: * 共有 **1750 億參數**,使用 FP16 格式需約 **320GB**。 * 至少需 **5 張 A100-80GB GPU** 才能進行推理。 ### 現有的壓縮方式 * 大多數研究集中於 **量化(quantization)**,如: * LLM.int8() * ZeroQuant * SmoothQuant * GPTQ 等 ### 剪枝(Pruning)作為輔助方案 * **剪枝** 是另一種模型壓縮方式,可移除: * 單一權重(非結構化 unstructured) * 整列或整欄(結構化 structured) * 雖然已廣泛應用於 CV 或小型語言模型,但: * **傳統剪枝方法多需 retraining(再訓練)** * 對 GPT 級別的大模型來說成本太高 * 現有 one-shot 方法雖不需 retraining,但在百億參數規模下仍過於耗時 ### SparseGPT 方法與貢獻 * **首次實現百億參數 LLM 的準確 one-shot 剪枝(無需再訓練)** * 可對 **OPT-175B、BLOOM-176B** 等開源大模型,在 **單顆 GPU** 上於數小時內完成剪枝 ### 方法核心 * 將剪枝問題轉換為 **超大規模稀疏回歸(sparse regression)問題** * 採用新的 **近似求解器(approximate solver)**,快速計算每層的局部更新 * 計算過程中不需使用全域梯度資訊,完全「local」 ### 實驗與發現 ### OPT-175B 的非結構化剪枝 ![image](https://hackmd.io/_uploads/HyrlYph8xx.png) * SparseGPT 可達 **60% 每層 sparsity**,幾乎不影響 perplexity * 傳統 one-shot baseline(如 Magnitude Pruning)僅在 **10% sparsity** 下保持準確,**超過 30% 就完全崩潰** ### 全模型與半結構化 sparsity ![image](https://hackmd.io/_uploads/SJBrtan8gx.png) * SparseGPT 可適用於 **2:4、4:8 半結構化剪枝**(硬體友好),儘管在小模型上略有精度損失 * **大模型反而更容易剪枝**(compressible),例如: * OPT-175B 與 BLOOM-176B 在 50% sparsity 下,perplexity 幾乎沒變化 * SparseGPT 也可與 **權重量化(如 GPTQ)結合** * 例如:**50% sparsity + 4-bit quantization → 幾乎無 perplexity 增加** ### 總結 | 特性 | 說明 | | -------- | ---------------------------- | | One-shot | 不需 retraining | | 適用範圍 | OPT-175B、BLOOM-176B 等百億級 LLM | | 支援格式 | 非結構化、2:4、4:8 半結構化 | | 可與量化結合 | GPTQ, INT4 | | 執行效率 | 單顆 GPU,數小時完成 | | 計算方式 | Local layer-wise 稀疏回歸,不用全域梯度 | ## Background ### 1. Post-Training Pruning(後訓練剪枝) * 定義: * 給定一個訓練完畢的模型 $\theta^\star$,加上少量 calibration data(校準資料),對模型進行壓縮(例如剪枝或量化)。 * 應用背景: * 最早被量化研究廣泛使用,近期也成功應用在剪枝上。 * 優點: * 不需要 retraining(再訓練),對大型模型來說是唯一實際可行的壓縮方式之一。 * 舉例引用: * 應用於剪枝的相關文獻有:Hubara 2021, Frantar 2022(OBC), Kwon 2022。 ### 2. Layer-Wise Pruning(逐層剪枝) * 動機: * 為了讓問題更可控,會將整體模型壓縮問題拆成一層一層處理(每層為一個子問題)。 * 剪枝的目標: * 找到一個 sparsity mask(稀疏遮罩) $\mathbf{M}\_\ell$(0 代表剪掉,1 代表保留) * 更新對應保留下來的權重 $\widehat{\mathbf{W}}\_\ell$ * 最小化以下誤差: $$ || \mathbf{W}_\ell \mathbf{X}_\ell - (\mathbf{M}_\ell \odot \widehat{\mathbf{W}}_\ell) \mathbf{X}_\ell ||_2^2 $$ * 實作流程: 1. 每層獨立處理(layer-wise) 2. 最後把每層剪枝後的結果「縫合」成整體模型 ### 3. Mask Selection 與 Weight Reconstruction * 核心問題: * 該目標函數涉及同時優化 mask 和權重,是一個 **NP-hard 問題**,不可有效準確求解 * 現有方法通常將問題分成兩步: 1. **Mask selection**:用某種準則選出要保留哪些參數(例如絕對值大小) 2. **Weight reconstruction**:在 mask 固定的前提下,最佳化其餘保留的參數 * 一旦 mask 確定,問題就變成簡單的 **線性平方誤差最小化問題(linear least-squares)**,可以有效求解 ### 4. 現有解法(Solvers)回顧 | 方法 | 概述 | 特點與限制 | | ------------------------------------------------- | ------------------------------------- | ------------------- | | **Kingdon 1997** | 使用反覆線性回歸來處理小網路 | 只適合非常小的模型 | | **AdaPrune**(Hubara 2021) | Magnitude-based 選擇權重 → 使用 SGD 重建保留權重 | 準確度與速度平衡良好,但仍不夠快 | | **Iterative AdaPrune**(Frantar 2022) | 分多輪進行 mask + 重建 → 每輪做 re-optimization | 準確率提升,但時間增加許多 | | **OBC (Optimal Brain Compression)**(Frantar 2022) | 每次移除一個權重、用封閉式公式完整重建其餘權重 | 精度最佳,但成本極高,無法延展到大模型 | ### 5. 現有方法的可擴展性瓶頸 * 以 AdaPrune 為例: * 壓縮 1.3B 參數模型就需數小時 * 對 175B 模型推估需數百小時(甚至數週) * OBC 等更精確的解法: * 計算量遠超 AdaPrune,甚至呈現超線性增長 * 總結: * **現有方法無法有效擴展到 100+B 參數等級的 LLM** ### 6. SparseGPT 的提出動機 * 為了解決「大模型剪枝不可能快速完成」的問題,作者提出: * **SparseGPT**:一種新的逐層(layer-wise)剪枝求解器 * 特點: * 使用封閉式公式的近似解法(approximate closed-form equations) * 能兼顧: * **執行效率(hours 級完成)** * **壓縮準確度(幾乎無精度下降)** * 可直接應用於 100B+ 模型,並在單顆 GPU 上運行 ## The SparseGPT Algorithm ### Fast Approximate Reconstruction(快速近似重建) ### 動機 * 在固定剪枝 mask $\mathbf{M}$ 的前提下,我們希望找出最佳的保留權重。 * 可逐行(row-wise)解以下 **稀疏重建問題**: $$ \mathbf{w^i}_{\mathbf{M_i}} = (\mathbf{X}_{\mathbf{M_i}} \mathbf{X}_{\mathbf{M_i}}^\top)^{-1} \mathbf{X}_{\mathbf{M_i}} (\mathbf{w}_{\mathbf{M_i}} \mathbf{X}_{\mathbf{M_i}})^\top $$ * $\mathbf{X}\_{\mathbf{M\_i}}$:代表 row $i$ 中未被剪掉位置的輸入 * 需對每個 row 都反推一個 **Hessian 矩陣 $\mathbf{H}\_{\mathbf{M\_i}}$** 並反轉(inverse) * 問題在於:每個 row 的 mask 不同 → 要各自反推 $\mathbf{H}\_{\mathbf{M\_i}}^{-1}$ → 成本高達: $$ O(d_\text{row} \cdot d_\text{col}^3) $$ * 對 Transformer,hidden dim = $d\_\text{hidden}$,時間複雜度會是 $O(d\_\text{hidden}^4)$ * 這在實務上難以接受 ### Row-Hessian 的挑戰 ![image](https://hackmd.io/_uploads/r1JxDR2Iex.png) * 每個 row 使用不同 mask → 無法共用 Hessian inverse * 若全部 row 使用同樣的 mask,就可以只算一次 Hessian 逆矩陣 * 但這會導致剪枝只能在整欄(column-wise)做,這種剪枝方式對準確率傷害大 ### Iterative 觀點下的解法(OBS 更新) * 透過 **Optimal Brain Surgeon (OBS)** 的觀點來看: * OBS 可用於 **逐個權重移除** 時,最佳化其餘權重以補償誤差 * 對於 quadratic loss,OBS 是精確的最佳解 * OBS 更新公式如下: $$ \delta_m = - \frac{w_m}{[\mathbf{H}^{-1}]_{mm}} \cdot \mathbf{H}^{-1}_{:, m}, \quad \varepsilon_m = \frac{w_m^2}{[\mathbf{H}^{-1}]_{mm}} $$ * 意義: * 可 **逐步剪枝每個權重**(不需一次決定整個 mask) * OBS 可重複使用於新的剩餘權重子集 → 可遞迴解出與閉式解相同的最終重建結果 ### Optimal Partial Updates(最佳局部更新) * 若不更新全部剩餘權重,而是只更新子集 $\mathbf{U} \subseteq \mathbf{M}$: * 可仍使用 OBS,精度可能略降,但大幅加速 * 若 $|\mathbf{U}|$ 小,Hessian 反轉成本大幅下降 ### Hessian Synchronization(Hessian 同步) 主要要解決以下問題: > 這是一種技巧,讓 **所有 row 共用一組或多組 Hessian inverse**,從而**大幅減少反覆計算 inverse 的成本**。 - 在精確的逐行重建中,我們每行都要: - 根據該行的剪枝 mask $\mathbf{M}_i$ 取出對應的輸入 $\mathbf{X}_{\mathbf{M}_i}$ * 然後計算對應的 Hessian: $$ \mathbf{H}_{\mathbf{M}_i} = \mathbf{X}_{\mathbf{M}_i} \mathbf{X}_{\mathbf{M}_i}^\top $$ * 並且反轉它來解重建公式 * 但每一行的 mask 都不同,代表要做成千上萬次 **矩陣反轉**,這對大模型根本不可行。 * 希望所有 row 都能共用一組(或一系列)Hessian inverse**,不管他們的 mask 是不是一模一樣。 ![image](https://hackmd.io/_uploads/HJyBD0n8ee.png) * 核心策略: 1. 對輸入維度順序固定為 $j = 1, \dots, d\_\text{col}$ 2. 建立一系列 index 子集 $U\_j$: $$ U_1 = \{1, \dots, d_\text{col}\}, \quad U_{j+1} = U_j - \{j\} $$ 3. 每個 $U_{j+1} = U_j - \{j\}$,就是從前一組移除一個 index,這樣會形成一連串「漸漸變小」的輸入子集 4. 逐步計算每個 $U\_j$ 的 Hessian 逆矩陣 * 更新方式: $$ (\mathbf{H}_{U_{j + 1}})^{-1} = \left(\mathbf{B} - \frac{1}{[\mathbf{B}]_{11}} \cdot \mathbf{B}_{:,1} \mathbf{B}_{1,:} \right)_{2:,2:} $$ * 每個 inverse Hessian 只算一次,可應用在所有 rows! #### 舉例(假設輸入維度是 4): | j | $U_j$ | | - | ------------ | | 1 | {1, 2, 3, 4} | | 2 | {2, 3, 4} | | 3 | {3, 4} | | 4 | {4} | * 對每個 $U_j$,計算對應的 Hessian: $$ \mathbf{H}_{U_j} = \mathbf{X}_{U_j} \mathbf{X}_{U_j}^\top \quad\text{再計算其逆:}\quad \mathbf{H}_{U_j}^{-1} $$ * 然後這一系列的逆矩陣會「**共用在所有 rows 的剪枝操作中**」,不再為每個 row 計算新 Hessian #### 如何快速更新 Hessian inverse? 根據矩陣逆的公式(Sherman-Morrison 或 Gaussian elimination),可以用下列方法**快速從 $\mathbf{H}_{U_j}^{-1}$ 得到 $\mathbf{H}_{U_{j+1}}^{-1}$**: $$ \mathbf{H}_{U_{j+1}}^{-1} = \left( \mathbf{B} - \frac{1}{[\mathbf{B}]_{11}} \cdot \mathbf{B}_{:,1} \mathbf{B}_{1,:} \right)_{2:,2:} $$ * 這是高效消除某一列與欄的技巧 * 從原始 $\mathbf{H}^{-1}$ 出發,只需執行 $d_\text{col}$ 次更新就能得到整條鏈 * 時間複雜度:**總共只要 $O(d_\text{col}^3)$**,遠小於原本 $O(d_\text{row} \cdot d_\text{col}^3)$ - 在剪掉某個 column(比如第 $j$ 欄)時: * 你用 $\mathbf{H}_{U_j}^{-1}$ 去計算該 column 被剪掉後,其他權重的 OBS 更新 * 所有 rows 中,**凡是有剪掉該位置的**,都可以共用這組 $\mathbf{H}_{U_j}^{-1}$ * 這樣每個 inverse 只用算一次,但可跨行使用 --- ### 計算複雜度(Computational Complexity) * 三個主要部分: 1. 初始 Hessian:$\Theta(n \cdot d\_\text{col}^2)$,$n$ = sample 數量 2. 逆矩陣序列:$O(d\_\text{col}^3)$ 3. 每列重建:$O(d\_\text{row} \cdot d\_\text{col}^2)$ * 總計: $$ O(d_\text{col}^3 + d_\text{row} \cdot d_\text{col}^2) = O(d_\text{hidden}^3) $$ * 比原本精確解法快了整整一個 $d\_\text{hidden}$ 效能等級 --- ### Weight Freezing 解釋(另類觀點) * 對於逐列逐欄剪枝來說,「compress」某個 weight 實質上就是 **凍結它(freezing)** * 一旦剪掉,就不能再更新 * 定義壓縮操作為: $$ \text{compress}(w^j)_i = \begin{cases} 0 & \text{if } j \notin M_i \\ w^j_i & \text{otherwise} \end{cases} $$ * 因此 SparseGPT 也可視為一種 **逐欄貪婪壓縮(column-wise greedy compression)** * 每次固定部分權重,保留其值,避免再更新 * 這讓 **剪枝 + 量化的合併壓縮**成為可能(後續會介紹) ## SparseGPT 進階設計與擴展 ### 1. Adaptive Mask Selection(自適應遮罩選擇) * 目前為止都假設剪枝 mask(哪些 weights 要剪)是固定的 * 傳統做法是「magnitude pruning」:剪掉數值最小的權重(如 AdaPrune) * 但近期研究指出:**剪枝過程中權重會變動、彼此相關性也會影響結果**,所以應該「在剪的同時決定要剪哪些」 ### SparseGPT 的做法:**自適應遮罩 + OBS 誤差排序** * 想法:依據每個 weight 的 **OBS 誤差貢獻 $\varepsilon$** 來動態決定要剪誰 * 不再一口氣對整層決定 mask,而是: 1. **每次處理一個 block(遮罩區塊)**,例如 $B_s = 128$ 個 columns 2. 根據每個 weight 的 OBS 誤差(根據 $\varepsilon = \frac{w^2}{[\mathbf{H}^{-1}]_{jj}}$)來排序,挑最容易剪的那幾個 3. 然後剪這些,並做重建,再進到下一個 block #### 好處 | 問題 | 解法 | | ------------------------ | -------------------------------- | | 原始方式會強迫 sparsity 在每欄都一樣多 | 自適應 mask 可以讓不同欄 sparsity 不同(更靈活) | | 無法考慮之前的 weight 更新 | OBS 誤差是逐步算的,能反映前面更新影響 | | 無法處理 outlier features | 某些欄太敏感,OBS 誤差能保護它們不被剪掉 | ### Extension to Semi-Structured Sparsity(支援半結構化剪枝) - 問題 - 為了硬體加速,像 2:4, 4:8 的 semi-structured pruning 格式在 NVIDIA GPU 有專門支援(如 Ampere Tensor Core) ### SparseGPT 的做法 * 將遮罩區塊大小 $B_s$ 設為 block size $m$ * 對每個 row: * 每 $m$ 個連續 weight 中選出 $n$ 個最小的 OBS 誤差 → 保留其餘 * 強制該 block 中有 $n$ 個 0 * 這種做法保證符合 n\:m 格式,例如: * 2:4 → 每 4 個中有 2 個是 0 * 4:8 → 每 8 個中有 4 個是 0 #### 限制 * 在 semi-structured 模式下,因為要硬性限制每個 block 的 sparsity,所以不能讓遮罩分佈不均 → **不能用太大的 $B_s$** ### 總算法 ![image](https://hackmd.io/_uploads/ry8VihTUex.png) ### 5. Weight Freezing 解釋 & GPTQ 整合 SparseGPT 的操作流程其實和 GPTQ 的「逐欄 greedy 量化策略」非常接近: * 每個被剪掉的權重就像是「凍結」(freezing)→ 不再更新 * 可與 GPTQ 的 lazy batch 更新、Cholesky decomposition 搭配使用 * 所有這些強化技巧都能應用在 SparseGPT 裡 ### 6. Joint Sparsification & Quantization(剪枝 + 量化單一流程) **傳統** 過去剪枝與量化通常是分開做: 1. 先剪枝 2. 再對剩下權重做量化 這會導致兩者之間「無法互相補償」誤差。 **SparseGPT 改成:** * 每次剪掉一部分權重,同時把剩下的做量化 * 在 OBS 更新時補償「剪掉 + 量化」帶來的誤差 * OBS 誤差補償公式改成: $$ \mathbf{E}_{:, j - i} \gets (\mathbf{W}_{:, j} - \mathbf{M}_{:, j} \cdot \text{quant}(\mathbf{W}_{:, j})) \, / \, [\mathbf{H}^{-1}]_{jj} $$ * 即: * 若某個 weight 被保留 → 就把它量化後拿來重建 * 若被剪掉 → 直接設 0 * 全流程在「**一輪通過(single pass)**」中完成剪枝 + 量化 → 幾乎無額外成本! ### SparseGPT 完整演算法整理 | 模組 | 核心貢獻 | | ------------------ | -------------------------------------------------- | | 自適應遮罩 | 動態選擇剪枝位置,考慮 OBS 誤差與 outlier 保護 | | Semi-structured 支援 | 符合硬體格式需求(2:4, 4:8),保留加速性 | | 單一剪枝 + 量化流程 | 在 single pass 中完成兩種壓縮,誤差可互補 | | GPTQ 技術整合 | 支援 lazy batch update、Cholesky、column-wise freezing | ## Experiments ### 🔧 1. 實驗環境(Setup) #### 系統與工具 * 使用 **PyTorch** 作為實作框架 * 搭配 **HuggingFace Transformers** 處理模型與資料 #### 訓練硬體 * 所有實驗都在 **單張 NVIDIA A100-80GB GPU** 上完成 * 能夠在 **4 小時內完成對 OPT-175B 模型的完整剪枝(sparsify)** #### 執行策略 * **Sequential Layer-wise pruning**: * 一層一層依序剪(非全模型一次剪) * 優點:大幅降低記憶體需求(避免同時載入全部層) #### Calibration 資料 * 取自 C4 corpus 的第一個分片,隨機選擇 128 段長度為 2048 的文本片段 * 特性: * 資料為網路文本(非任務特定) * 保持 **zero-shot pruning 環境**,不會有任務偏見 ### 2. 模型與資料集(Models, Datasets & Evaluation) #### 模型選擇 * 主要使用 Meta 的 **OPT 模型系列**,涵蓋: * 小型:125M、350M、1.3B * 大型:2.7B、6.7B、13B、30B、66B、175B * 也納入 **BLOOM-176B** 作為額外驗證 #### 評估資料集 * 主要用於語言建模任務的標準資料: * **WikiText2 (raw)** * **Penn Treebank (PTB)** * **C4 validation subset** * 額外用於 zero-shot 任務準確率評估的資料集: * **Lambada**(長文本完形填空) * **ARC-Easy / ARC-Challenge**(常識問答) * **PIQA**(物理常識) * **StoryCloze**(故事邏輯判斷) #### 評估原則 * **重點放在稀疏模型與原始 dense 模型的「相對表現」** * 絕對值可能會因資料前處理差異有出入 * 但相對精度變化具穩定性與比較價值 * 所有結果(dense/sparse)使用完全相同的程式碼測量 * 包含 perplexity 計算與 zero-shot 任務 #### 工具鏈 * perplexity:使用 HuggingFace 官方方法,搭配 full stride * zero-shot:使用 GPTQ 提供的實作(基於 EleutherAI eval-harness) ### 4. 比較基準方法(Baselines) | 方法 | 特性 | 適用範圍 | | --------------------- | ------------------- | ---------------------- | | **Magnitude Pruning** | 根據權重大小逐層剪枝(簡單但延展性強) | 可套用到最大模型(如 175B) | | **AdaPrune** | 考慮重要性 + 重建,精度佳 | 僅適用於 < 1B 模型(受限於資源與時間) | | **SparseGPT** | 本研究提出的新法 | 可擴展至 175B 並達成優異速度與精度 | ### 實驗結果 ### 1. Pruning vs. Model Size 模型越大越容易剪 #### 實驗設定 * 對 **OPT 全系列模型**進行實驗,剪枝範圍為所有 linear 層(不含 embedding 和 head) * 比較三種 sparsity: * **50% unstructured** * **4:8 semi-structured** * **2:4 semi-structured**(最嚴格) ### 結果 ![image](https://hackmd.io/_uploads/BJVjeaa8lx.png) ![image](https://hackmd.io/_uploads/HJLngTaIxl.png) * **Magnitude Pruning 表現崩潰**: * 隨著模型越大(尤其是 >1B),簡單的權重大小剪枝(magnitude)導致 **困惑度爆炸** * 和 vision model 的剪枝結果形成強烈對比(vision 模型常能安全剪掉 50% 以上) * **SparseGPT 效能穩定甚至提升**: * OPT-2.7B 僅損失約 1 perplexity * OPT-66B 幾乎無損失 * OPT-175B 在某些資料集甚至略優於原始模型(dataset-specific) * **AdaPrune 雖比 magnitude 好,但仍不如 SparseGPT** * 速度也慢很多(AdaPrune 對 1.3B 需約 4.3 小時,而 SparseGPT 可在同時間內剪完 66B 或 175B) #### 結論:大型 LLM 越大越容易剪(可能因 overparameterization) ### 2. Sparsity Scaling for 100B+ Models ![image](https://hackmd.io/_uploads/HJ_EbTaLxg.png) ![image](https://hackmd.io/_uploads/HyrB-6T8le.png) 對兩個最大模型:**OPT-175B** 與 **BLOOM-176B**,進行不同 sparsity 等級的實驗: #### OPT-175B * **Magnitude Pruning**: * 超過 10% sparsity 就會造成重大精度損失 * **SparseGPT**: * 可安全到 **60% sparsity** * 到 **80% sparsity** 還能保持 reasonable perplexity * 移除近 **100B 個參數** 幾乎不影響結果 #### BLOOM-176B * magnitude pruning 稍微好一些(30% 可接受) * 但 SparseGPT 仍優於其,能達成 **50% sparsity** 且精度相當 #### 結論 * magnitude pruning 在 40\~60% sparsity 時,困惑度急升到 >100(模型崩潰) * SparseGPT 即使到 80% 也可穩定輸出 ### 3. ZeroShot 評估實驗(泛化能力) ![image](https://hackmd.io/_uploads/BJMMz66Lex.png) * **Magnitude 完全崩潰(表現近隨機)** * **SparseGPT 表現接近甚至優於原始 dense 模型** * 部分特例如 Lambada 在 2:4 剪枝後還略升(解釋:zero-shot noise,但平均仍穩定) ### Joint Sparsification & Quantization (聯合剪枝與量化) ![image](https://hackmd.io/_uploads/r1RkQ6pIel.png) 將 **剪枝(Sparsity)** 與 **量化(Quantization)** 結合,以同時達成: * **運算加速**(剪掉多餘參數 → 減少乘法數) * **記憶體節省**(每個權重位元數下降) ### 空間節省對比 * 假設模型: * 採用 **50% sparsity**(只保留一半權重) * 剩餘權重用 **4-bit 量化** * 並使用一個 **bitmask** 表示哪些權重為非零 **這樣的儲存空間 ≈ 純量化 3-bit 模型** | 模型大小 | 方法 | Perplexity | | -------- | -------------------- | ---------- | | OPT-175B | GPTQ (3-bit) | 8.68 | | OPT-175B | SparseGPT 50% + 4bit | 8.29 | * 在 OPT-2.7B 以上的模型中,**SparseGPT(剪 + 量)優於純 3-bit 量化** * 同樣也測試了: * 2:4 + 4bit → 8.55 * 4:8 + 4bit → 8.85 * 顯示:**在 semi-structured sparsity 基礎上加入 4bit quantization,perplexity 只增加約 0.1**(非常小) ### Sensitivity & Partial N:M Sparsity ![image](https://hackmd.io/_uploads/rk1dX6aLle.png) ### 實驗設定 以 OPT-175B 和 BLOOM-176B 為例: * 將 **2/3 的層**套用 **2:4 剪枝** * 剩下的 1/3 層「不剪」 * 測試兩種選擇方式: 1. **不剪某一類型層**(如 attention / FFN1 / FFN2) 2. **不剪某一段位置的層**(前 1/3、中間 1/3、後 1/3) ### 結果觀察 * 不同模型對不同 layer type 敏感度不一 * 但明顯趨勢:**後段層(last third)較敏感** * 若要保留一段不剪 → **保留最後 1/3 精度最好** ### 總結 | 項目 | 觀察 | | -------------------- | --------------------------------------------------- | | 聯合壓縮 | 50% sparsity + 4bit quantization 表現 **優於純 3bit 量化** | | semi-structured + 量化 | 即便是 2:4 或 4:8,再加上 4bit 量化也只會增加 0.1 的 perplexity | | 部分剪枝建議 | 若不能全部剪,可選擇 **後段層不剪**,效果最佳 | | 剪枝流程優勢 | SparseGPT 可在 **一次剪枝過程中輸出多版本模型**,提升部署彈性 | ## 結論 本研究提出了一種全新的 **後訓練剪枝方法**,名為 **SparseGPT**,專為大規模 GPT 系列語言模型設計,具備以下幾項突破: ### 一次性剪枝(One-shot Pruning) * **不需要再訓練(no retraining)**: * SparseGPT 能直接在已有的 GPT 模型上進行剪枝 * 僅需少量校準資料(calibration data) * 就能 **一次性(one-shot)達成高 sparsity** * 成效良好(Accuracy preserved): * 在 **perplexity**(語言建模精度)與 **Zero-shot 任務**(泛化能力)下都表現穩定 ### 剪枝最大規模模型 * 模型規模:OPT-175B 與 BLOOM-176B(超過 1000 億參數) * 剪枝結果: * 達到 **50–60% sparsity** * 移除超過 **1000 億個權重** * 精度幾乎無損 #### 利用 GPT 模型的 **高參數冗餘性(overparameterization)** * 原始 dense 模型的「鄰近區域」中,存在可行的高 sparsity 子模型 * 即使 **不使用 gradient 訊息**,也能夠直接搜尋到這些稀疏模型 * 剪枝後的模型輸出與 dense 模型非常接近,維持語言理解能力 ### 更大的模型反而更容易剪枝 * **實驗觀察**: * 相同 sparsity(如 50%)情況下: * 小模型會有明顯精度下滑 * 大模型的精度幾乎不變 * 解釋: * 可能是因為大模型「冗餘更多」,有更多空間能剪枝 * 顯示未來在 compress 更大模型時 **具高度潛力與應用價值** ## Ablation Studies ### 實驗對象與目標 * 模型:**OPT-2.7B** * 資料集:**raw-WikiText2** * 剪枝比例:固定為 **50% sparsity** * 目的:探索幾個核心參數對 SparseGPT 效果的影響 ### 1. 校準樣本數量(Calibration Samples) ![image](https://hackmd.io/_uploads/ByakU6aLgx.png) * 測試不同數量的 2048-token calibration segments(2、4、8、16...) * **結果**: * 即使樣本數很少(例如 4 條樣本)也能產出「可用結果」 * 但樣本數量 **愈多效果愈好** * 增益在超過某個點後 **漸漸趨緩(飽和)** * **選定設定**: * 使用 **128 段樣本** 是效能與資源的良好平衡點 ### 2. Hessian 抑制因子(Hessian Dampening) ![image](https://hackmd.io/_uploads/Bks-L668xl.png) * 使用類似 GPTQ 的技巧:在計算 Hessian inverse 前對對角線加上阻尼 * 減少數值不穩定問題 * 減少過擬合 * 測試範圍:`0.0001` 到 `1.0`(乘上 Hessian 對角平均值) * **結果**: * `0.001` \~ `0.1` 效果接近 * 抑制過強(如 `1.0`)→ 會造成精度顯著下降 * **選定設定**:使用 **`0.01`(1%)** 作為穩定且保守的選擇,適用於最大模型 ### 3. Mask 選擇區塊大小(Mask Selection Blocksize) ![image](https://hackmd.io/_uploads/rkcmIpT8ge.png) * 探討 mask 計算時的 column block size 對剪枝效果的影響 * `1` 表示 column-wise 計算 * `4096+` 表示接近整個行 * **結果**: * 極端值(太小或太大)會導致效果變差 * 最佳範圍落在 **幾百的區塊大小** * **選定設定**:使用 **`128`** * 準確率穩定 * 與 lazy weight update 批次大小相符 → **簡化實作** ### 4. 隨機性敏感度(Random Seed Sensitivity) * 重複相同實驗(50% sparsity)**5 次**,但使用不同的 calibration data sampling 隨機種子 * 結果:`13.52 ± 0.075` perplexity * **結論**: * SparseGPT 對 calibration data 的隨機性 **不敏感** * 與其他 post-training 方法一致(如 GPTQ、OBC 等) ### 小結 | 項目 | 發現 | 結論 | | -------------- | ---------------------- | ---------- | | Calibration 數量 | 多樣本會更準,但 128 已足夠 | 計算效率與準確平衡點 | | Hessian 抑制 | 太高會降準確,但在合理區間效果穩定 | 建議值:0.01 | | Blocksize | column-wise 太細、整塊太粗都不佳 | 建議值:128 | | Random Seed | 小幅度浮動,不敏感 | 高穩定性 | ### Approximation Quality SparseGPT 在進行剪枝時,為了節省時間與記憶體成本,**並不重建完整權重矩陣**,而是採用「**Partial Update Approximation(部分更新近似)**」。 ### 實驗設計 * 模型:OPT-2.7B * Sparsity:50% * 分析項目:比較每層的 **重建誤差**: * `SparseGPT` 的近似重建 * vs. * 同樣 mask 與 Hessian 下的 **精確重建(exact reconstruction)** ### 實驗發現 ![image](https://hackmd.io/_uploads/Syopv6a8ll.png) * 絕大多數 layer 的重建誤差僅比精確版本多出 **約 20%** * 特例:早期 attention out-projection layers 的誤差較高(為 outlier) * 在後半部如 `fully-connected-2` layers,誤差甚至僅約 **10%** * 推測原因:輸入維度非常大 → 即使只看部分相關性,資訊損失也不大 #### Perplexity 計算方式 * 完全依照 HuggingFace 的官方標準流程 hfperplexity: 1. **將資料集中所有樣本串接起來** 2. 使用對應的 tokenizer 編碼為 tokens 3. 切割為不重疊的 **2048-token segments** * 這是 LLM 的最大上下文長度 4. 每段輸入模型,計算其 **平均 loss** 5. 將 loss 指數化,即為 **perplexity** ### Additional Results ### 不同資料集上的剪枝難度(PTB & C4) ![image](https://hackmd.io/_uploads/SyFUqapIgx.png) 對 OPT 各模型在 PTB 與 C4 資料集上進行同樣的剪枝實驗,延伸主文中在 WikiText2 的分析: * **趨勢一致**:SparseGPT 的效能在各資料集上皆顯著優於 Magnitude 與 AdaPrune。 * **資料集特異性差異**: * 在 WikiText2 中,50% sparsity 時大型模型甚至有略微 **perplexity 改善**(可能來自 regularization 效果), * 但此現象未在 PTB 或 C4 重現,因此被視為資料集特有。 ### Sparsity Acceleration 目標:驗證 SparseGPT 剪枝後的模型,**在不需 retraining 的情況下是否能實現推論加速**,並評估目前現成工具對稀疏模型的支持程度。 > 此處僅為初步實驗,未針對特定模型作最佳化;作者認為未來仍有大量加速潛力。 ### CPU 推論加速:Unstructured Sparsity * **環境設定**: * 使用 [DeepSparse](https://neuralmagic.com/deepsparse/) 引擎(支援 unstructured sparsity) * 模型:`OPT-2.7B` * 設備:Intel i9-7980XE(18 核) * 任務:單批次 400 tokens 的 end-to-end 推論 * 備註:DeepSparse 的 dense 模型本身就比 ONNXRuntime 快 1.5 倍 #### 結果(相對於 dense DeepSparse): | Sparsity Level | Speedup | | -------------- | ------- | | 40% | 1.57× | | 50% | 1.82× | | 60% | 2.16× | **幾乎達到理論最佳值**:代表在 CPU 上執行稀疏 Transformer 推論,已經相當實用。 ### GPU 推論加速:2:4 Structured Sparsity * NVIDIA 自 Ampere 架構起,支援 2:4 結構化稀疏加速(透過稀疏矩陣乘法 kernel) * 工具:NVIDIA CUTLASS(使用 profiler 尋找最佳 kernel) * 對照:cuBLAS dense kernel(PyTorch 用的 dense matmul) * 條件: * 模型:OPT-175B 中典型矩陣大小 * 批次大小:2048 tokens #### 測試結果: | Layer Type | Dense (ms) | 2:4 Sparse (ms) | Speedup | | ---------- | ---------- | --------------- | ------- | | Q/K/V/Out | 2.84 ms | 1.59 ms | 1.79× | | FC1 | 10.26 ms | 6.15 ms | 1.67× | | FC2 | 10.23 ms | 6.64 ms | 1.54× | * 所有主層類型都有明顯加速 * 儘管理論上是 **2×** 加速,實際約 **1.5–1.8×**,已是非常可觀的提升 * **整體端到端加速** 可能會略低,因為仍存在 attention、I/O 等非 matmul 成分