Co naprawdę zapisuje Q4_K_M: kwantyzacja wag od podstaw
Q4_K_M bajt po bajcie, prosto ze źródeł ggml: superbloki po 256 wag, 6-bitowe skale i minima, które tensory dostają Q6_K i dlaczego, ile kosztuje wartość odstająca i gdzie wchodzi obrót. Do tego rozmiar i szybkość modelu 27B w każdym k-quancie.
„Q4_K_M” jest na każdej stronie z lokalnymi modelami do pobrania i większość ludzi wybiera go, bo wybierają go wszyscy. Poniżej to, co naprawdę jest w pliku, bajt po bajcie, prosto ze źródeł ggml. I dlaczego „4 bity” w prawdziwym modelu wychodzą na mniej więcej 5 bitów na wagę.
Jedno równanie
Każdy schemat zapisuje małą liczbę całkowitą q na wagę plus kilka wspólnych liczb na blok wag, które zamieniają ją z powrotem na liczbę zmiennoprzecinkową:
w ≈ d · q symetrycznie (Q4_0, Q8_0)
w ≈ d · q − m z minimum, żeby siatka mogła być przesunięta (k-quanty)
d to krok między poziomami. Przy b bitach zakres bloku dzieli się na 2^b poziomów. Zaokrąglenie do najbliższego poziomu zostawia błąd o rozkładzie jednostajnym, ze średnim błędem kwadratowym około d² / 12.
Każdy dodatkowy bit zmniejsza d o połowę, czyli błąd kwadratowy 4×. Przejście z 4 bitów na 3 czterokrotnie go zwiększa; przejście z 4 na 8 zmniejsza go 256×. Tę wymianę robi każdy format.
Dlaczego bloki
Jedna skala na cały tensor pozwoliłaby jednej dużej wadze ustawić d dla milionów innych. Dlatego wagi kwantyzuje się w małych blokach, każdy z własną skalą. Skala to narzut: 16-bitowa skala na 32 wagi kosztuje 0,5 bita na wagę. Najprostsze formaty, prosto z ggml-common.h:
typedef struct {
ggml_half d; // skala
uint8_t qs[QK4_0 / 2]; // 32 × 4 bity
} block_q4_0; // 18 bajtów na 32 wagi = 4,5 bita na wagę
typedef struct {
ggml_half d; // skala
int8_t qs[QK8_0]; // 32 × 8 bitów
} block_q8_0; // 34 bajty na 32 wagi = 8,5 bita na wagę
Q4_0 to więc format 4,5-bitowy. Skala to cena za to, że wartości odstające zostają lokalne.
K-quanty: skale dla skal
K-quanty zachowują małe bloki, ale kwantyzują same liczby na blok, wewnątrz superbloku 256 wag:
typedef struct {
ggml_half d; // skala superbloku dla skal podbloków
ggml_half dmin; // skala superbloku dla minimów podbloków
uint8_t scales[12]; // 8 skal + 8 minimów, po 6 bitów
uint8_t qs[QK_K/2]; // 256 × 4 bity
} block_q4_K; // 144 bajty na 256 wag = 4,5 bita na wagę
256 wag to 8 podbloków po 32. Każdy podblok dostaje własną 6-bitową skalę i 6-bitowe minimum, a te są skalowane dwiema liczbami FP16 wspólnymi dla superbloku:
w = d · skala_j · q − dmin · min_j q ∈ [0, 15], j = podblok
Narzut jest ten sam co w Q4_0, 0,5 bita na wagę. Ale każde 32 wagi dostają teraz asymetryczną siatkę, czyli skalę i przesunięcie dopasowane do własnego zakresu, zamiast symetrycznej siatki wokół zera. Te same bity, lepsze dopasowanie.
Reszta rodziny, z tego samego pliku nagłówkowego:
| Typ | Bajty na 256 wag | Bity na wagę | Układ |
|---|---|---|---|
| Q3_K | 110 | 3,4375 | 16 × 16, skale 6-bitowe |
| Q4_K | 144 | 4,5 | 8 × 32, skale i minima 6-bitowe |
| Q5_K | 176 | 5,5 | Q4_K plus jeden wysoki bit na wagę |
| Q6_K | 210 | 6,5625 | 16 × 16, skale 8-bitowe |
| Q8_0 | 272 (8 bloków po 34) | 8,5 | bloki po 32 wagi, skala FP16 |
„_M”: nie każdy tensor dostaje 4 bity
„Q4_K_M” to przepis, a nie typ. Kwantyzator (src/llama-quant.cpp) wybiera typ dla każdego tensora:
output.weight: Q6_K. To projekcja, która produkuje logity każdego tokenu.attn_viffn_down: Q6_K w połowie warstw. To pierwsza ósma, ostatnia ósma i co trzecia warstwa pomiędzy:i < n/8 || i ≥ 7n/8 || (i − n/8) % 3 == 2.- Połączone tensory QKV, jeśli model je ma: Q5_K.
- Cała reszta: Q4_K.
Dlaczego te tensory? Przy grouped-query attention attn_v jest kilka razy mniejszy niż attn_q. Źródła wspominają, że w modelu 70B jest 8× mniejszy, więc dodatkowe bity są prawie darmowe. Wzór warstw to heurystyka wbudowana w kwantyzator: dodatkowe bity na obu końcach sieci i w regularnych odstępach w środku.
Wynik: model 27B w Q4_K_M ma około 17 GB. To 17 × 8 / 27 ≈ 5,0 bita na wagę średnio, a nie 4.
Ile kosztuje wartość odstająca
Krok bloku wyznacza jego największa waga. Wstaw jedną wagę 10× większą od reszty do bloku 32 wag i d rośnie 10×. Pozostała 31 wag ma teraz 100× większy błąd kwadratowy, bo błąd skaluje się z d².
Są trzy obrony, od najtańszej do najdroższej:
- Małe bloki, żeby wartość odstająca psuła tylko swoje 32 wagi. Robi to już każdy format powyżej.
- Macierz ważności.
llama-imatrixzapisuje, jak mocno aktywuje się każdy kanał wejściowy na przykładowym tekście. Kwantyzator minimalizuje wtedy błąd tam, gdzie ma największe znaczenie, a nie równomiernie. Liczy się to najbardziej poniżej 4 bitów. - Obrót. Pomnóż przez macierz ortonormalną, zwykle randomizowaną macierz Hadamarda, a energia wartości odstającej rozkłada się na cały blok. QuaRot i QuIP# robią to z wagami i aktywacjami, żeby jedno i drugie zeszło do 4 bitów. To ten sam trik, dzięki któremu działa 4-bitowy cache KV; opisuje go notatka o cache KV.
W plikach GGUF z kwantyzacją samych wag zwykle wystarczają dwie pierwsze. Obrót staje się konieczny, gdy kwantyzujesz to, co liczy się w trakcie działania, czyli aktywacje albo cache KV, bo nie ma przebiegu offline, na którym dałoby się skalibrować.
Rozmiar i szybkość modelu 27B
Liczby poniżej zakładają, że każdy tensor ma ten sam typ. Prawdziwe mieszanki _M wychodzą trochę wyżej. Sufit przepustowości pochodzi z notatki o dekodowaniu: jedna RTX 4090 przy około 1008 GB/s, jeden token na przebieg, same wagi.
| Typ | Bity na wagę | Wagi | Sufit dekodowania |
|---|---|---|---|
| FP16 | 16 | 54,0 GB | 18,7 przebiegu/s |
| Q8_0 | 8,5 | 28,7 GB | 35,1 przebiegu/s |
| Q6_K | 6,5625 | 22,1 GB | 45,5 przebiegu/s |
| Q5_K | 5,5 | 18,6 GB | 54,3 przebiegu/s |
| Q4_K | 4,5 | 15,2 GB | 66,4 przebiegu/s |
| Q3_K | 3,4375 | 11,6 GB | 86,9 przebiegu/s |
24 GiB karty RTX 4090 to 25,8 GB. To, co zostaje po wagach, jest Twoim budżetem na cache, a rozmiary cache na token z notatki o cache KV zamieniają go na długość kontekstu.
Mniej bitów to mniej bajtów na przebieg, więc kwantyzacja kupuje szybkość, a nie tylko pamięć. Dlatego „mieści się” i „jest szybkie” zwykle wskazują ten sam format.
Jak wybieram
- Q4_K_M to domyślny wybór dla modeli 20–35B na jednym GPU 24 GB. Daje najlepszą jakość na bajt i wciąż zostawia miejsce na cache przy długim kontekście.
- Małe modele dostają więcej bitów. Przy tej samej liczbie bitów zwykle tracą więcej na kwantyzacji, a są na tyle małe, że Q5_K_M albo Q6_K kosztuje niewiele.
- Trzymaj build Q8_0 jako punkt odniesienia. Mierz względem niego dywergencję KL (
llama-perplexity --kl-divergence), a potem uruchom własne ewaluacje zadań. Perplexity na ogólnym tekście nie powie Ci, czy JSON wywołań narzędzi wciąż się parsuje. - Poniżej 4 bitów używaj macierzy ważności i ponownie uruchamiaj ewaluacje. Tam formaty przestają być wymienne.
Źródła: układy bloków i reguły tensorów Q4_K_M pochodzą z ggml/src/ggml-common.h i src/llama-quant.cpp w llama.cpp, w wersji ze źródeł w moim forku. Kwantyzacja z obrotem: Ashkboos i in., QuaRot (2024); Tseng i in., QuIP# (2024).