Kwantyzacja LLM: uruchamianie modeli o 160 GB na laptopie z minimalnymi stratami
Parametry — wagowe wartości sieci neuronowej — decydują o objętości LLM w pamięci. Każdy parametr przechowywany jest jako liczba zmiennoprzecinkowa. Najprostszy blok: wejście mnożone przez wagę daje wyjście. W rzeczywistych modelach setki warstw z tysiącami węzłów prowadzą do miliardów parametrów.
Na przykład sieć z 2 wejściami, 3 warstwami po 4 węzły i 2 wyjściami ma 64 parametry. Rozszerzając do setek tysięcy węzłów — uzyskujemy tryliony wag. Histogramy rozkładu parametrów popularnych modeli pokazują: 99% wartości zbliża się do zera, w zakresie [-0.5, 0.5].
Reprezentacja liczb w pamięci
Komputery przechowują float32: 1 bit znaku, 8 bitów wykładnika, 23 bity mantysy. Zakres ±3,4×10³⁸ z 7 cyfr znaczących. Rozkład nierównomierny: gęstszy wokół zera, rzadszy na końcach. Idealne dla LLM, gdzie parametry są małe.
Float16: 1+5+10 bitów, dokładność 3–4 cyfry, zakres ±65504. Bfloat16 (1+8+7): szeroki zakres, 2–3 cyfry. Float8/Float4 — eksperymentalne, z 3–4 bitami mantysy.
Aproksymacja sinusa: float32 gładka, float4 krokowa z widocznymi błędami.
Zasady kwantyzacji
Kwantyzacja to kompresja z utratą: odwzorowanie liczby zmiennoprzecinkowej na mniejszy zbiór wartości. Proste zaokrąglanie (round-to-nearest) z bfloat16 do float4 niszczy model: wagi zerują się, wyjście to zero. Przyczyna: zakres float4 [-3,3] nie pasuje do wag [-0.89,0.16].
Symetryczna kwantyzacja
Skalowanie danych pod zakres całkowity. Wzór: scale = max_abs / (2^(bits-1) - 1). Kwantyzacja: round(value / scale), dekwantyzacja: quantized * scale.
Przykład kodu w JavaScript:
function quantize({ values, bits }) {
const vmax = Math.max(...values.map(Math.abs));
const qmax = 2 ** (bits - 1) - 1;
const scale = vmax / qmax;
return {
values: values.map((v) => Math.round(v / scale)),
scale,
};
}
function dequantize({ values, scale }) {
return values.map((v) => v * scale);
}
Dla values = [-0.89, 0.16, 0.08, -0.13, 0.16, -0.54], bits=4:
- quantized: [-7,1,1,-1,1,-4], scale≈0.127
- dequantized: [-0.89,0.127,0.127,-0.127,0.127,-0.509]
- średnia błąd: 18%
Wyjście modelu po 4-bitowej kwantyzacji: odchylenie 30% od oryginału, ale 4 razy mniej pamięci.
Asymetryczna kwantyzacja
Ulepszenie symetrycznej: uwzględnia min/max osobno. Zakres [min, max] skalowany do [qmin, qmax]. Wzór:
- offset = min
- scale = (max - min) / (qmax - qmin)
- quantized = round((value - offset) / scale)
Efektywnie wykorzystuje przestrzeń: dla danych asymetrycznych (więcej ujemnych) strona dodatnia nie jest tracona. Średni błąd spada do 5–10%.
Zastosowanie do tensorów: kwantyzuj po kanałach lub grupach (per-group quantization) dla minimalizacji błędów w aktywacjach.
Ocena jakości po kwantyzacji
Mierz perplexity na zestawie walidacyjnym lub metryki zadania (BLEU, ROUGE). Benchmarki:
- Qwen-3-Coder-Next 4-bit: perplexity +7% vs FP16
- Prędkość: x2 na GPU bez jąder tensorowych
| Format | Rozmiar (GB) | Perplexity | Prędkość (tok/sek) |
|--------|-------------|------------|------------------|
| FP16 | 159.4 | 1.00 | 1.0 |
| INT8 | 39.8 | 1.05 | 1.8 |
| INT4 | 19.9 | 1.09 | 2.1 |
Co warto wiedzieć
- Kwantyzacja skraca model 4–8 razy bez ponownego uczenia.
- Symetryczna dla danych symetrycznych, asymetryczna dla rozkładów skośnych.
- Kwantyzacja po grupach (po 128 elementów) balansuje precyzję i szybkość.
- Obsługa w llama.cpp, bitsandbytes: INT4/INT8 gotowe do użycia.
- Testuj na zadaniami końcowych: kodowanie, pytania i odpowiedzi.
— Editorial Team
Brak komentarzy.