JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

給模型瘦身

一個能登頂排行榜、卻讓每次請求都貴得離譜的模型,只是研究演示,不是產品。本篇講清三大槓桿——量化、剪枝、蒸餾——它們讓你用小模型的預算跑出接近大模型的質量,也講清每一個槓桿背後藏著的誠實代價。

模型為什麼一開始就太大

走到這一級,你已經會訓練模型、把它部署上線、並盯著它的延遲。但你訓練出來的那個模型,幾乎總是比你應該部署的那個更大。一個擁有幾百億個參數大語言模型,把每個權重存成 16 位或 32 位的數字;再乘上每生成一個詞元就要觸碰的那幾十億個權重,你就得到了推理成本那道殘酷的算術——存權重要的記憶體、搬權重要的頻寬、乘權重要的能耗,而且每來一次請求都得重付一遍。

不過模型之所以胖,是有原因的,而且不是浪費。更大的容量讓它更容易被*訓練*——多出來的參數給了梯度下降更大的迴旋餘地去找到一個好解,而推動過去十年的縮放定律也表明:隨著參數和資料增加,損失會平滑下降。本篇每一項技術背後的關鍵洞見就是:你*學會*一件事所需的規模,遠大於你*運行*它所需的規模。瘦身,就是在大樓蓋好之後把腳手架拆掉的藝術。

量化:每個數字少花幾個位元

最便宜、最通用的槓桿是[[quantization-ml|量化]]:用更少的位元來儲存和計算每個權重。一個以 32 位浮點訓練出來的權重,所攜帶的精度遠超模型實際用得上的,於是你把它四捨五入到一個更小的格式——8 位整數(INT8)已是常規,4 位如今在大語言模型上也很常見。從 16 位降到 4 位,記憶體佔用大約縮小到四分之一,而這往往就是「一塊GPU裝得下」和「裝不下」之間的分水嶺。

別把它和你之前學過的混合精度訓練搞混。那裡,低精度是為了加快運算,同時保留一份全精度副本來維持訓練穩定。而*訓練後量化*發生在模型凍結之後:你把一段段浮點取值映射到一個小小的整數網格上,為每一組權重存一個縮放因子,再在運行時即時重建出一個近似的浮點數。其中的門道在於如何選這個網格——天真的四捨五入會毀掉準確率,因為少數罕見的離群權重數值極大,會把其他所有權重都擠進同一個格子裡。

32-bit float weight   ->   4-bit integer + per-group scale
  0.0731  0.0024 ...        [ 9, 0, 14, 3, ... ]   x  scale=0.0081
  ~4 bytes each            ~0.5 byte each
  full precision          ~8x smaller, tiny rounding error
量化把每個權重四捨五入到一個粗糙的整數網格上,每組共享一個縮放因子,在運行時重建出近似值。

能壓到多狠?對多數模型,INT8 幾乎無損;4 位會損失一點點質量,但通常划得來;再往下,準確率往往會斷崖式下跌。如果連小心翼翼的四捨五入都傷得太重,就用*量化感知訓練*:把捨入操作模擬進訓練迴路裡去微調模型,讓梯度下降學出能扛住這道擠壓的權重。回報是實打實的:量化後的模型需要更少記憶體、搬更少位元組,在對整數友好的硬體上還跑得更快——延遲和吞吐量同時改善。

剪枝:把沒用的權重丟掉

[[pruning|剪枝]]從另一個角度對付體積:它不是讓每個數字變便宜,而是乾脆刪掉一些數字。許多訓練好的權重都趴在零附近,對輸出幾乎毫無貢獻,於是你把它們置零並跳過。*非結構化*剪枝把單個權重一個個置零——能砍掉很大一部分而準確率損失不大,但一個稀疏散落的矩陣很難讓GPU加速,所以你省了記憶體卻沒怎麼省速度。

在生產中通常更划算的是*結構化*剪枝:你移除的是整個單元——整個神經元、注意力頭,或Transformer中的整層。剩下的模型既更小*又*稠密,普通硬體不用任何特殊技巧就能跑得更快。經典做法是迭代式的:剪掉一片,微調讓倖存者恢復,度量,再重複。一次性剪得太猛,模型就再也緩不過來;循序漸進地剪,你往往能削掉三分之一的網絡而幾乎不傷元氣。

蒸餾:把大模型懂的教給小模型

[[knowledge-distillation|知識蒸餾]]是最雄心勃勃的槓桿。你不去壓縮已訓練好的模型,而是從頭訓練一個全新的、更小的*學生*去模仿一個大的*教師*。訣竅在於學生模仿的是什麼:不只是教師的最終答案(硬標籤),而是它在所有選項上的完整機率分布——也就是*軟標籤*。當教師說「80% 是貓、15% 是狗、5% 是狐狸」時,這個分布就告訴學生:貓和狗比貓和狐狸更像。這些細微的梯度,每個樣本所攜帶的信號遠多於一個獨熱答案,所以小學生從中學得更快也更好。

這正是許多你真正負擔得起去部署的小而快模型背後的路徑——當教師是基礎模型、學生是一個緊湊可部署的模型時,同樣的思路就叫模型蒸餾。不過要誠實地認清上限:蒸餾遷移的是教師*在你給它看的資料上的行為*,而不是什麼智能的精華。學生會繼承教師的盲點和偏見,在遠離蒸餾資料的任務上,它就是會力不從心。一個蒸餾出來的學生,是某一項能力的忠實回聲,而非一顆微縮的通用心智。

組合起來:在實踐中實現廉價推理

這些槓桿可以疊加。一條常見的生產流水線,會先把一個能幹的教師蒸餾成一個更小的學生,再對學生做結構化剪枝,然後把結果量化到 4 位或 8 位——在體積和成本上拿到三重相乘的收益。把這一切統稱起來就是模型壓縮,而這門手藝是經驗性的:每一步都需要自己那一輪度量,因為損失會累乘,有時還會以出人意料的方式相互作用。

  1. 先定好預算和底線:你必須達到的延遲、記憶體和成本上限,以及你不能跌破的準確率下限(看最壞情況切片,而不只是平均值)。
  2. 先試量化——它是最便宜、最可逆的收益,單靠 INT8 往往就能把你壓到預算之內,且損失微乎其微。
  3. 若還不夠,就做結構化剪枝、每剪一刀後微調一次,或者從手頭的模型蒸餾出一個更小的學生。
  4. 透過一個可移植的執行時來編譯並部署最終模型,讓它在你的目標硬體上(邊緣或伺服器)跑得快。
  5. 端到端重新度量,並在上線後持續盯著——隨著資料漂移,壓縮後的模型可能以與母模型不同的方式退化。

兩個實用的搭檔讓閉環成型。一種可移植的服務格式,比如 ONNX Runtime,讓你把壓縮後的模型匯出一次,就能在 CPU、GPU 和各類加速器上運行而無需重寫——而足夠小的模型還打開了邊緣部署的大門,直接在手機或感測器上運行,完全不用往返雲端。這一切都不是「設好就不管」:要把你壓縮後的模型置於和其他模型一樣的監控之下,因為你在測試中躲過的那道懸崖,在真實世界裡依然可能出現。