Trefon.
← Wszystkie notatki 11 min

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.

Jacek Trefon
  • deep dive
  • kwantyzacja
  • GGUF
  • llama.cpp
Kod ↗

„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:

TypBajty na 256 wagBity na wagęUkład
Q3_K1103,437516 × 16, skale 6-bitowe
Q4_K1444,58 × 32, skale i minima 6-bitowe
Q5_K1765,5Q4_K plus jeden wysoki bit na wagę
Q6_K2106,562516 × 16, skale 8-bitowe
Q8_0272 (8 bloków po 34)8,5bloki 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_v i ffn_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:

  1. Małe bloki, żeby wartość odstająca psuła tylko swoje 32 wagi. Robi to już każdy format powyżej.
  2. Macierz ważności. llama-imatrix zapisuje, 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.
  3. 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.

TypBity na wagęWagiSufit dekodowania
FP161654,0 GB18,7 przebiegu/s
Q8_08,528,7 GB35,1 przebiegu/s
Q6_K6,562522,1 GB45,5 przebiegu/s
Q5_K5,518,6 GB54,3 przebiegu/s
Q4_K4,515,2 GB66,4 przebiegu/s
Q3_K3,437511,6 GB86,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

  1. 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.
  2. 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.
  3. 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.
  4. 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).

Masz podobny problem?

Opisz swój problem

30-minutowa rozmowa techniczna z inżynierem, który to buduje. Bez działu sprzedaży, bez slajdów.