Jak zlevnit inference: KV Cache, batching a kvantizace
Stejný model, stejná odpověď za 8,8 s i 78,9 s. Pak INT4 měřené třemi způsoby, ne jen tvrzené.
Na této stránce
Stejný model, na stejném stroji, odpovídá na stejnou otázku stejnými 48 token. Oba výstupy jsou identické token po token — ověřeno, ne předpokládáno.
with a key-value cache: 8.85 s ( 6.01 tokens/second)
without a key-value cache: 78.95 s ( 0.60 tokens/second)Změnil se jediný argument: use_cache=False. Nic na modelu, prompt, samplingu ani aritmetice není jiné a druhý běh za svou námahu není přesnější. Je devětkrát pomalejší pro nic.
To je tvar této kapitoly. Všechno v ní — cache, batch, kvantizované váhy — je pokus přestat platit za práci, která nemění odpověď, nebo zjistit, kolik stojí levnější odpověď. Kapitola 10 stanovila ceník trénování. Tohle je ceník strany, za kterou platíte navždy: nasazený model utratí zhruba FLOPs za každý token, který vydá, při každém požadavku, po zbytek svého života.
Kam se poděl čas druhého běhu
Odkaz na sekci: Kam se poděl čas druhého běhuAby vygeneroval token, decoder-only transformer vezme celou dosavadní sekvenci, prožene ji každou vrstvou a z poslední pozice přečte rozdělení pravděpodobnosti. Pak připojí zvolený token a udělá to znovu. Ten popis je správný a přesně to dělá pomalý běh.
Je to také enormně plýtvavé a důvodem je kauzální maska z kapitoly 9. Vektory key a value pro pozici 7 se počítají ze vstupu pozice 7 a pozic před ní. Když dorazí pozice 8, pozice 7 ji nemůže vidět — to znamená kauzalita — takže key a value pozice 7 jsou přesně stejná čísla jako předtím. Pomalý běh je přesto v každém kroku počítá znovu.
Tak je uložte. Toto úložiště je key-value cache, nejdůležitější optimalizace při servírování jazykových modelů:
out = model(prompt_ids, use_cache=True) # prefill: the whole prompt
past = out.past_key_values
nxt = out.logits[:, -1].argmax(-1, keepdim=True)
for _ in range(n - 1):
out = model(nxt, past_key_values=past, use_cache=True)
past = out.past_key_values
nxt = out.logits[:, -1].argmax(-1, keepdim=True)Podívejte se, co se uvnitř smyčky předává modelu: nxt, jeden token. Ne sekvence. Query nového token se dívá přes attention na všechny uložené key a uložené key se nikdy změnit neměly. Tohle není aproximace — smyslem je kontrola identického výstupu výše. Cache nevyměňuje kvalitu za rychlost; maže redundantní aritmetiku.
Aby bylo škálování čistě vidět, odstraňte transformer a změřte jednu attention hlavu s , jeden krok generování spočítaný oběma způsoby:
| token v kontextu | přepočítat všechno | s cache | poměr | matice skóre |
|---|---|---|---|---|
| 128 | 0,59 ms | 0,062 ms | 10x | 65,536 B vs 512 B |
| 256 | 1,20 ms | 0,163 ms | 7x | 262,144 B vs 1,024 B |
| 512 | 7,03 ms | 0,078 ms | 90x | 1,048,576 B vs 2,048 B |
| 1024 | 17,31 ms | 0,114 ms | 152x | 4,194,304 B vs 4,096 B |
| 2048 | 59,83 ms | 0,214 ms | 279x | 16,777,216 B vs 8,192 B |
| 4096 | 236,18 ms | 0,284 ms | 832x | 67,108,864 B vs 16,384 B |
Pravý sloupec je příčina. Přepočet v každém kroku vytvoří plnou attention matici — z rámečku o asymptotické notaci v kapitole 9, placené jednou za token. S cache místo toho vytvoříte řádek : při 4 096 token jde o 67 MB skóre proti 16 KB.
Počítání multiply-accumulates místo milisekund odstraní ze sporu konkrétní stroj. Pro vygenerování token od studeného startu:
| vygenerované token | s cache | přepočet | poměr |
|---|---|---|---|
| 128 | 2,6 M | 192,0 M | 73x |
| 512 | 23,1 M | 7,36 G | 318x |
| 2048 | 293,7 M | 392,6 G | 1 336x |
V jednom kroku je verze s cache lineární v kontextu a verze bez cache kvadratická; sečteno přes celé generování jde o proti , přičemž poměr roste bez omezení. Devítinásobný rozdíl z úvodu byl měřen na 48 token — ještě pod prvním řádkem této tabulky.
Cache také mění to, co musí být v paměti. Na 8GB GPU v notebooku při generování 256 token v fp16, když vezmeme špičku alokátoru a odečteme rezidentní váhy:
| špičková pracovní paměť | |
|---|---|
| s cache | 21,8 MB |
| přepočet | 181,7 MB |
8,3krát více paměti, utracené za pomalejší produkci stejných token. Tohle je slib z kapitoly 5, přicházející z nečekaného směru: tam musela reverse-mode autodiff držet každý mezivýsledek pro backward pass a aktivace dominovaly paměti při trénování. Při inference žádný backward pass není a není kvůli němu co uchovávat — takže paměti místo toho dominuje cache, a ta je záměrná volba, ne nevyhnutelný náklad.
Prefill a decode jsou dva různé stroje
Odkaz na sekci: Prefill a decode jsou dva různé strojePodívejte se znovu na rychlý běh: jeho první token se choval jinak než zbylých čtyřicet sedm.
prefill, 40 prompt tokens : 1.0224 s -> 25.6 ms per token
decode, 47 steps : 0.1665 s mean per stepPrompt stál 25,6 ms na token a každý vygenerovaný token stál 166 ms. Stejný model, stejný hardware, stejné váhy, šestinásobný rozdíl na token — a opačným směrem, než většina lidí čeká. Prompt je ta levná část. Generování se dělí do dvou fází se skutečně odlišnou fyzikou:
Prefill
Odkaz na sekci: PrefillJeden forward pass přes celý prompt. Každý token se zpracuje paralelně, takže každá matice vah se z paměti načte jednou a násobí se maticí stovek vektorů token — matrix-matrix product se spoustou aritmetiky na přesunutý byte, přesně to, pro co je GPU stavěné. Prefill je compute-bound a jeho náklad je zhruba lineární v délce prompt.
Decode
Odkaz na sekci: DecodeJeden forward pass na token, batch jedna a sekvence jedna. Každá matice vah se stále načítá z paměti celá a násobí se jedním vektorem — matrix-vector product s téměř žádnou aritmetikou na přesunutý byte. Decode je memory-bandwidth-bound a jeho náklad na token téměř nezávisí na délce kontextu.
Obě poloviny jsou měřitelné. Prefill, jeden průchod přes token:
| token v prompt | sekundy | ms na token |
|---|---|---|
| 16 | 0,3515 | 21,97 |
| 32 | 0,5254 | 16,42 |
| 64 | 1,0491 | 16,39 |
| 128 | 1,6552 | 12,93 |
| 256 | 3,0965 | 12,10 |
Decode, jeden token proti cache o velikosti :
| uložené token v cache | ms pro jeden token |
|---|---|
| 16 | 110,05 |
| 64 | 97,57 |
| 256 | 108,53 |
| 1024 | 103,86 |
Druhou tabulku si přečtěte dvakrát. Přechod od 16 token kontextu k 1 024 — čtyřiašedesátkrát více historie, přes kterou běží attention — nezměnil náklad kroku ničím měřitelným. Attention proti cache je skutečná práce, ale trpasličí vedle fixních nákladů na protažení půl miliardy vah paměťovou sběrnicí, aby vznikl jeden vektor. Tento fixní náklad je důvodem všeho v další části.
Tyto dvě fáze jsou původem dvou čísel, která reportuje každý serving systém. Time to first token je v podstatě prefill a roste s prompt, proto se dlouhá konverzace rozjíždí pomalu. Tokens per second je a je zhruba konstantní, proto pak odpověď plyne rovnoměrně. Chat, který začíná pomalu a potom plynule streamuje, není trik vykreslování. Jsou to tyto dvě tabulky.
Cache je také účet
Odkaz na sekci: Cache je také účetCache vyměňuje aritmetiku za paměť a paměť, kterou chce, není malá. Pro každý token v kontextu drží každá vrstva jeden key vektor a jeden value vektor na každou key-value hlavu:
Dvojka je za key a value; všechno ostatní je architektura. Pro model měřený v celé této kapitole — 24 vrstev, 14 query hlav, 2 key-value hlavy, rozměr hlavy 64 — je to v fp16 bytů na token.
Vzorce v tomto oboru mají ve zvyku mýlit se o faktor dvě, takže to raději ověřte proti alokátoru, než tomu věřit:
KV cache tensors per layer: (1, 2, 295, 64) float16
measured: 3,624,960 bytes for 295 tokens = 12,288 bytes/token
formula : 2 * 24 * 2 * 64 * 2 = 12,288 bytes/tokenPřesné, a zůstává to přesné napříč každým zkoušeným tvarem:
| batch | kontext | změřená cache | predikce | špičková pracovní paměť |
|---|---|---|---|---|
| 1 | 512 | 6,0 MB | 6,0 MB | 15,4 MB |
| 1 | 16,384 | 192,0 MB | 192,0 MB | 207,3 MB |
| 1 | 65,536 | 768,0 MB | 768,0 MB | 793,7 MB |
| 8 | 4,096 | 384,0 MB | 384,0 MB | 401,5 MB |
| 32 | 2,048 | 768,0 MB | 768,0 MB | 794,2 MB |
| 64 | 1,024 | 768,0 MB | 768,0 MB | 797,0 MB |
| 128 | 512 | 768,0 MB | 768,0 MB | 816,4 MB |
Poslední tři řádky si zaslouží druhý pohled. Třicet dva uživatelů s 2 048 token každý, šedesát čtyři s 1 024, sto dvacet osm s 512 — cache má v každém případě 768 MB, protože všechny tři drží 65 536 token. Cache závisí pouze na celkovém počtu rezidentních token, ne na tom, jak jsou rozděleny mezi uživatele. Tato skutečnost je základem části o batching.
Odkud pocházejí MQA a GQA
Odkaz na sekci: Odkud pocházejí MQA a GQAKapitola 9 představila multi-query a grouped-query attention a důvod odložila do této kapitoly. Důvodem je tento vzorec, konkrétně v něm.
Standardní multi-head attention dává každé query hlavě vlastní key a value hlavy. Zdejší model má 14 query hlav; s plnou multi-head attention by jeho cache byla bytů na token — 84 KB místo 12 KB, přesně sedmkrát více, poměr query hlav ke key-value hlavám.
Multi-query attention1 to bere až na limit: všechny query hlavy sdílejí jednu key-value hlavu. Grouped-query attention2 je kompromis, který zvítězil — několik key-value hlav, každá sdílená skupinou query hlav — protože ztráta kvality u MQA byla skutečná a u GQA není. Ani jedno nekupuje žádnou aritmetiku. Existují proto, aby tento vzorec vydělily celým číslem, a rozšířily se průmyslem ve chvíli, kdy dlouhé kontexty udělaly z cache závazné omezení.
A to se stane rychle. U modelu třídy 7B s 32 vrstvami a 8 key-value hlavami o rozměru 128 je cache v fp16 128 KB na token:
| token kontextu | jeden uživatel | 8 uživatelů | 64 uživatelů |
|---|---|---|---|
| 4,000 | 0,49 GB | 3,91 GB | 31,2 GB |
| 32,000 | 3,91 GB | 31,25 GB | 250,0 GB |
| 128,000 | 15,62 GB | 125,00 GB | 1,000,0 GB |
| 1,000,000 | 122,07 GB | 976,56 GB | 7,812,5 GB |
Vlastní váhy tohoto modelu mají v fp16 13,0 GB, číslo v tabulce na konci kapitoly. Takže při kontextu 128 000 token je cache jednoho uživatele větší než model. Tohle je aritmetika, kterou kapitola 16 převádí na peníze, a proto dlouhá konverzace není jen pomalá — po celou dobu života požadavku zabírá pevný kus stroje.
Batching: číslo, které roste, a číslo, které klesá
Odkaz na sekci: Batching: číslo, které roste, a číslo, které klesáDecode je memory-bound: váhy se táhnou přes sběrnici, aby vznikl jeden token, a aritmetické jednotky zahálejí. Tak do stejného kroku vložte více práce. Spusťte několik požadavků najednou a váhy, přečtené jednou, obslouží všechny. Měřeno na stejném modelu, každý požadavek drží 64-token cache a dekóduje jeden token:
| batch | latence na krok | propustnost | latence vs B=1 |
|---|---|---|---|
| 1 | 0,1286 s | 7,78 tok/s | 1,00x |
| 2 | 0,1839 s | 10,88 tok/s | 1,43x |
| 4 | 0,1909 s | 20,95 tok/s | 1,49x |
| 8 | 0,2781 s | 28,76 tok/s | 2,16x |
| 16 | 0,3430 s | 46,64 tok/s | 2,67x |
| 32 | 0,6302 s | 50,78 tok/s | 4,90x |
Dva pravé sloupce čtěte proti sobě, protože jsou celou pointou. Přechod z jednoho požadavku na šestnáct vynásobí propustnost 6,0 a čekání libovolného jednotlivého požadavku vynásobí 2,67. Batch zlepšil server a zhoršil každého uživatele.
To není chyba, kterou lze odladit pryč; je to samotný trade-off a na každé straně má jméno. Latence je to, co zažívá člověk čekající na odpověď. Propustnost je to, čím se dělí faktura. Žádné nastavení nezlepší obojí.
Všimněte si také, kde se to zastaví. Od 16 do 32 získá propustnost 9 %, zatímco latence se téměř zdvojnásobí: krok přestal být memory-bound a stal se compute-bound, a za tímto kolenem už batch nekupuje nic. Každé nasazení takové koleno má; jeho polohu musíte změřit na tom svém, ale jeho existenci ne.
Statický batching promarní většinu toho, co vyhraje
Odkaz na sekci: Statický batching promarní většinu toho, co vyhrajeNaivní způsob batching je nasbírat požadavků, spustit je společně a vrátit, až jsou všechny hotové. Jenže nekončí společně: některé odpovědi mají dvacet token a jiné pět set. Pevný batch běží, dokud neskončí jeho nejdelší člen, a každý dokončený požadavek do té doby dál zabírá svůj slot a přidává padding.
Vezměte 64 požadavků s realistickým vychýlením délek výstupu — medián 18 token, nejdelší 231, celkem 1 874 — a nasimulujte obě politiky při změřeném nákladu na krok pro osm slotů:
| politika | wall clock | propustnost | průměrná latence na požadavek | promarněné slot-kroky |
|---|---|---|---|---|
| statické batche po 8 | 176,9 s | 10,6 tok/s | 83,2 s | 3,214 |
| continuous, 8 slotů | 109,0 s | 17,2 tok/s | 8,1 s | 0 |
Propustnost se zlepší 1,6x. Průměrná latence se zlepší více než desetkrát, protože při statickém batching požadavek, který skončil ve čtyřech krocích, stále čeká na 231-token souseda, než o něm kdokoli uslyší.
Continuous batching3 je oprava a je tak jednoduchá, jak zní: batch není skupina, ale sada slotů, a slot, který se uvolní, přijme další požadavek z fronty hned v dalším kroku. Plánovač pracuje v granularitě jednoho token, ne jednoho požadavku. Každý serving stack v produkci to dnes dělá.
Má to druhou polovinu, kterou je cache. Sloty, které přicházejí a odcházejí, nechávají paměť cache fragmentovanou a rezervovat pro každý slot jeho maximální možný kontext plýtvá většinou rezervace. PagedAttention4 si půjčuje odpověď z operačních systémů: uložte cache do bloků pevné velikosti s tabulkou bloků pro každou sekvenci, takže cache sekvence může být fyzicky rozptýlená, ale logicky souvislá — což také umožňuje dvěma sekvencím se sdíleným prefixem sdílet bloky, které ho drží. Na tom stojí vLLM a proto je serving engine alokátor paměti s připojeným transformer.
Kvantizace a první věc, která se pokazí
Odkaz na sekci: Kvantizace a první věc, která se pokazíDruhou polovinou účtu jsou samotné váhy. Půl miliardy parametrů po čtyřech bytech je 1,98 GB; po dvou bytech 0,99 GB; po jednom bytu 0,49 GB. Méně bitů na váhu zmenší model na disku, zmenší ho v paměti a — protože decode je bandwidth-bound — zrychlí každý krok, protože je potřeba přesunout méně bytů.
Nejjednodušší schéma je symetrická kvantizace podle absolutního maxima a vejde se do tří řádků:
qmax = 2 ** (bits - 1) - 1
scale = W.abs().max() / qmax
Wq = torch.round(W / scale).clamp(-qmax - 1, qmax)
W_hat = Wq * scale # dequantizedZvolte měřítko tak, aby se největší váha mapovala na největší celé číslo, vydělte, zaokrouhlete, uložte celá čísla a měřítko. Rekonstruujte vynásobením zpět. Není na tom nic chytrého a funguje to — až do chvíle, kdy ne.
Měřeno na skutečných vahách modelu: všech 168 projekčních matic, 357,8 milionu parametrů, relativní chyba :
| schéma | průměrná relativní chyba | nejhorší matice |
|---|---|---|
| INT8, jedno měřítko pro celou matici | 0,0400 | 0,1487 |
| INT8, jedno měřítko na výstupní řádek | 0,0100 | 0,0149 |
| INT4, jedno měřítko pro celou matici | 0,6026 | 0,9931 |
| INT4, jedno měřítko na výstupní řádek | 0,1790 | 0,2589 |
| INT4, jedno měřítko na skupinu 128 | 0,1323 | 0,1992 |
| NF4, jedno měřítko na blok 64 | 0,0952 | 0,1205 |
| INT3, jedno měřítko na skupinu 128 | 0,3044 | 0,4123 |
| INT2, jedno měřítko na skupinu 128 | 0,7790 | 0,8076 |
Čtvrtý řádek je kolaps. Relativní chyba 0,99 na nejhorší matici znamená, že rekonstrukce si z originálu v podstatě neponechala nic — matice byla nahrazena šumem přibližně správné velikosti. Příčina je vidět ve stejném experimentu na jedné matici:
model.layers.12.mlp.down_proj.weight (896 x 4864)
mean |w| 0.01386 std 0.01822 max |w| 0.43945 max/std 24.1
weights beyond 6 sigma: 692 of 4,358,144 (0.016 %)Jedna váha ze šesti tisíc leží za šesti směrodatnými odchylkami a největší je 24 odchylek daleko. S jedním měřítkem pro celou matici tato jedna váha určuje velikost kroku pro všech 4,3 milionu vah. Při 8 bitech existuje 256 kroků a typická váha stále dopadne na smysluplný. Při 4 bitech jich je 16, krajní je rezervovaný pro hodnotu, kterou nemá skoro nic, a běžné váhy — tedy všechny — se zaokrouhlí na dvě nebo tři různé úrovně.
Všechno po tomto řádku je stejná oprava v různé granularitě: dejte měřítku menší území. Měřítko na výstupní řádek vydělí chybu 3,4; měřítko na skupinu 128 po sobě jdoucích vah ji vydělí znovu. Nákladem je účetnictví — 16bitové měřítko na skupinu 128 je bitů na váhu místo 4 — a získá zpět většinu mezery.
NF4 na to jde z druhé strany.5 Úrovně nemusí být rovnoměrně rozestoupené. Váhy uvnitř bloku jsou přibližně normálně rozdělené, takže zvolte šestnáct úrovní jako kvantily normálního rozdělení: husté u nuly, kde váhy skutečně jsou, řídké v ocasech, kde nejsou. Stejné čtyři bity, stejné blokové škálování, menší blok — 4,25 bitu na váhu proti 4,125 u skupiny 128 — a změřená chyba klesá z 0,1323 na 0,0952, o 28 % níž. Část je jemnější blok a zbytek umístění úrovní tam, kde je hmota; oddělit obojí by vyžadovalo třetí řádek.
Outlier features
Odkaz na sekci: Outlier featuresBox o floating-point v kapitole 2 skončil slibem: že tato kapitola bude kvantizovat váhy na 8 a 4 bity a najde hrstku outlier features, které se odmítají nechat zmáčknout. Tady jsou a vysvětlují, proč „prostě zaokrouhlit čísla“ u aktivací nikdy nemohlo fungovat.
Váhy výše se chovaly špatně. Aktivace jsou úplně jiná liga. Vezměte obyčejný 84-token prompt, zachyťte residual stream v každé vrstvě a změřte největší velikost, které dosáhne každý z 896 rozměrů:
| vrstva | největší |h| | největší |h| mediánového rozměru | poměr | rozměry nad 6x mediánu |
|---|---|---|---|---|
| 1 | 6,19 | 0,339 | 18x | 2 |
| 4 | 1543,48 | 1,550 | 996x | 34 |
| 8 | 1571,63 | 1,498 | 1049x | 36 |
| 12 | 1575,03 | 1,546 | 1019x | 34 |
| 16 | 1579,60 | 1,617 | 977x | 32 |
| 20 | 1577,98 | 2,361 | 668x | 24 |
| 24 | 204,44 | 10,760 | 19x | 12 |
Rozměr 62 dosahuje 1 579,6, zatímco mediánový rozměr nikdy nepřekročí 1,6. Není to náhoda jednoho token ani jedné vrstvy: stejný rozměr je tam ve vrstvě 4 a stále tam je ve vrstvě 20, s téměř stejnou hodnotou. To jsou outlier features,6 a jsou systematické — vlastnost natrénovaného modelu, ne vstupu.
Histogram těchto 896 maxim po rozměrech ve vrstvě 16 dělá tvar nezaměnitelným:
0 - 1 | ######################################## 254
1 - 2 | ######################################## 283
2 - 4 | ######################################## 226
4 - 8 | ######################################## 93
8 - 16 | ################## 18
16 - 32 | ######### 9
32 - 64 | ####### 7
64 - 128 | ##### 5
128 - 256 | 0
256 - 512 | 0
512 - 1024 | 0
1024 - 4096 | # 1Devět set rozměrů v úhledné hromádce pod 8, vůbec nic po tři oktávy, a potom jeden jediný rozměr na vzdáleném konci. Teď tento tensor kvantizujte na INT8 a spočítejte, co se stane:
| schéma | relativní chyba | použité různé celočíselné úrovně, celý tensor |
|---|---|---|
| jedno měřítko pro celý tensor | 0,1083 | 14 z 256 |
| jedno měřítko na token (na řádek) | 0,0433 | 158 |
| celý tensor, 1 outlier rozměr ponechán v fp32 | 0,0442 | 48 |
| celý tensor, 4 outlier rozměry ponechány v fp32 | 0,0279 | 57 |
| celý tensor, 16 outlier rozměrů ponecháno v fp32 | 0,0085 | 102 |
Čtrnáct úrovní z 256. Měřítko nastavila hodnota 1 579,6, takže každý krok má šířku 12,44 a typická aktivace — medián velikosti 0,26, devadesátý devátý percentil 2,51 — nemá kam dopadnout. Po rozměrech je to ještě ostřejší:
single tensor-wide scale = 12.4378
dim 826 (max |h| = 4.77): 1 distinct level out of 256
dim 336 (max |h| = 1.62): 1 distinct level out of 256
dim 96 (max |h| = 0.69): 1 distinct level out of 256
after excluding the top 4 dimensions, scale = 0.5749 (22x smaller)
dim 826: 8 levels dim 336: 4 levels dim 96: 3 levelsJedna úroveň. Celý rozměr, každý token, kvantizovaný na stejné číslo. Bylo přiděleno osm bitů a použilo se zhruba nula; model čtoucí tyto aktivace dostane konstantu.
Toto měření je ospravedlněním každé techniky, kterou lidé skutečně používají:
Držte outliers mimo. LLM.int8()6 rozkládá násobení matic: rozměry s extrémními velikostmi se počítají v 16 bitech, všechno ostatní v INT8, a poloviny se sečtou. Tabulka výše je účtenka — odstranění čtyř rozměrů zmenší chybu téměř čtyřikrát. SmoothQuant7 místo toho přesune obtíž: vydělí aktivace faktorem na kanál a vynásobí jím odpovídající sloupec vah, čímž produkt zůstane nezměněn a outlier se přesune z tensor, který ho neunese, do toho, který může.
Vyberte zaokrouhlení, ne jen zaokrouhlujte. Nic výše se neptá, k čemu matice je. GPTQ8 kvantizuje sloupec po sloupci a po každém upraví zbývající sloupce v plné přesnosti, aby kompenzovaly už zavedenou chybu — minimalizuje chybu výstupu vrstvy na skutečných vstupech, ne chybu jejích vah. AWQ9 si všímá, že malý zlomek kanálů vah je mnohem důležitější než zbytek, najde je ze statistik aktivací a před kvantizací je zvětší, aby dopadly na jemnější úrovně. Obě techniky potřebují kalibrační sadu; ani jedna nepotřebuje gradienty.
Zobrazit podrobnosti
GGUF a co s tím má společného souborový formát.
GGUF není metoda kvantizace; je to kontejner, který používá llama.cpp, a zmatek ve srovnáních gguf vs gptq vzniká z toho, že se obě věci berou jako stejný druh věci. GGUF drží tensory, tokenizer, metadata architektury a chat šablonu v jednom memory-mappable souboru a nese v sobě rodinu blokových schémat — názvy jako Q4_K_M kódují bity na váhu, velikost bloku a to, zda jsou některé tensory ponechány ve vyšší přesnosti.
Inženýrský rozdíl, na kterém záleží: GPTQ a AWQ produkují váhy optimalizované pro GPU kernel, zatímco schémata GGUF se levně dekódují na CPU se souborem namapovaným, ne načteným. Proto stejný nominální „4bitový 7B model“ existuje v obou světech s různými velikostmi a různou kvalitou, a proto poctivé srovnání nikdy není formát — je to měření níže, spuštěné na vašem vlastním úkolu.
Co kvantizace skutečně stojí, změřeno
Odkaz na sekci: Co kvantizace skutečně stojí, změřenoSkoro každý článek o kvantizaci končí v předchozí části: vysvětlí metodu, ocituje kompresní poměr a prohlásí, že kvalita je „do značné míry zachována“. Kapitola 4 byla o tom, jak si nelhat, tak to zjistěme.
Stejný model, váhy kvantizované na místě každým schématem, potom tři měření: perplexita na 2 048 token odložené anglické prózy — zde koncept tohoto kurzu, proto repozitář nahrazuje pevnou public-domain knihou a tiskne tabulku stejného tvaru s jinými čísly — baterie 16 krátkých faktických otázek se známými odpověďmi při greedy decoding a podíl token, na kterých kvantizovaný model souhlasí s modelem v plné přesnosti při identickém kontextu.
| schéma | průměrná chyba vah | perplexita | baterie otázek | souhlasí s fp32 |
|---|---|---|---|---|
| fp32 (reference) | 0,0000 | 23,08 | 13/16 | 100,0 % |
| INT8 na tensor | 0,0400 | 23,58 | 13/16 | — |
| INT8 na řádek | 0,0100 | 22,96 | 13/16 | 98,6 % |
| INT4 na tensor | 0,6026 | 365,416,000 | 0/16 | — |
| INT4 na řádek | 0,1790 | 46,18 | 6/16 | 58,3 % |
| INT4 skupina 128 | 0,1323 | 31,08 | 10/16 | 71,5 % |
| NF4 blok 64 | 0,0952 | 24,55 | 11/16 | 84,7 % |
| INT3 skupina 128 | 0,3044 | 213,09 | 0/16 | 5,6 % |
| INT2 skupina 128 | 0,7790 | 26,325,436 | 0/16 | 0,0 % |
Čtyři věci v této tabulce stojí za jasné vyslovení.
Správně udělané INT8 je zdarma. INT8 na řádek má skóre 22,96 proti 23,08 reference — mezera jedna část ze dvou set, což je šum a má se číst jako „identické“. Směr šumu není stabilní: na public-domain korpusu v repozitáři vycházejí stejná dvě schémata 22,24 proti 22,18: polovina této vzdálenosti a opačným směrem. S modelem v plné přesnosti souhlasí na 142 ze 144 vygenerovaných token. Čtvrtina paměti proti fp32 referenci, polovina proti fp16, které byste skutečně nasadili, a žádný zjistitelný náklad. INT8 udělané nedbale je také skoro zdarma: jedno měřítko na matici stojí 0,5 bodu perplexity a žádné odpovědi v baterii. Osm bitů odpouští dost na to, aby granularita sotva záležela, což je přesně důvod, proč lidé zobecňují z INT8 na INT4 a narazí.
INT4 s jedním měřítkem na tensor model zničí. Perplexita 365 milionů: ne zhoršené, anihilované. Granularita je pak celá hra — per-tensor 365,416,000, per-row 46,18, per-group-of-128 31,08, NF4 24,55. Stejné čtyři bity na váhu, faktor patnáct milionů mezi nejhorším a nejlepším.
Perplexita je hrubý nástroj a baterie ještě hrubší. Mezi NF4 a group-128 INT4 je mezera perplexity 6,5 bodu a baterie se liší o jednu otázku — a interval spolehlivosti z kapitoly 4 říká, že jedna otázka ze šestnácti nerozlišuje vůbec nic. Existuje ostřejší ukázka než interval: spusťte stejnou baterii s vypnutou standardní penalizací opakování modelu, což je to, co greedy decoding skutečně znamená, a tyto dva řádky si prohodí místa. Jedna otázka ze šestnácti není malý efekt, je to žádný efekt. Platí i varování z kapitoly 8: perplexita je srovnatelná jen mezi modely sdílejícími tokenizer, takže číslo z cizího článku nelze porovnat s vaším.
Sloupec shody je nejostřejší ze tří, a téměř zdarma: spusťte model v plné přesnosti greedy, pak se kvantizovaného zeptejte v každé pozici, co by zvolil při stejném prefixu. Má 144 nezávislých pozorování místo 16, nepotřebuje ground truth a degraduje hladce tam, kde baterie degraduje skokově. Je to také přesně veličina, kterou potřebuje další část.
Toto je slib, který kapitola 1 dala o této kapitole, doručený včas: matematika říká, že 4bitový model je možný, a inženýrství rozhodne, zda je použitelný.
Speculative decoding
Odkaz na sekci: Speculative decodingKapitola 12 to oznámila a účet nechala tady.
Myšlenka vychází přímo z rozdělení prefill/decode. Ověřit navrženou sekvenci token stojí jeden forward pass přes pozic — matrix-matrix product, sotva dražší než průchod přes jednu. Takže:
Malý, levný model vygeneruje kandidátních token autoregresivně.
Verify
Odkaz na sekci: VerifyVelký model spustí jeden forward pass přes všech kandidátů najednou a vytvoří, co by řekl na každé pozici.
Accept
Odkaz na sekci: AcceptPonechte nejdelší prefix, na kterém se oba shodnou, plus token, který velký model dodá zdarma při první neshodě. Zbytek zahoďte a začněte znovu.
Výstupní rozdělení se nemění. U greedy decoding je to zřejmé — token se přijme jen tehdy, pokud by ho vytvořil target. U samplingu to vyžaduje upravené pravidlo přijetí a Leviathan et al. dokazují, že výsledné rozdělení je přesně rozdělení target.10 Toto je druhá přesná optimalizace v této kapitole.
Všechno tedy závisí na míře přijetí , která je měřitelná — je to sloupec shody výše, proto se tam počítal. Použijeme každý kvantizovaný model jako draft pro target v plné přesnosti, přes 144 generovaných pozic:
| draft model | přijetí | nejdelší přijatý běh | očekávané token na target pass, |
|---|---|---|---|
| fp32 (samotný target) | 100,0 % | 48 | 5,00 |
| INT8 na řádek | 98,6 % | 48 | 4,86 |
| NF4 blok 64 | 84,7 % | 20 | 3,69 |
| INT4 skupina 128 | 71,5 % | 13 | 2,85 |
| INT4 na řádek | 58,3 % | 7 | 2,24 |
| INT3 skupina 128 | 5,6 % | 2 | 1,06 |
| INT2 skupina 128 | 0,0 % | 0 | 1,00 |
Očekávaný počet token přijatých na ověřovací průchod při délce draft je
a čisté zrychlení to dělí vlastním nákladem draft, zlomkem target na token:
| přijetí | , | , | , | , |
|---|---|---|---|---|
| 30 % | 1,19x | 1,02x | 0,79x | 0,79x |
| 50 % | 1,61x | 1,38x | 1,08x | 1,11x |
| 70 % | 2,31x | 1,98x | 1,54x | 1,78x |
| 90 % | 3,41x | 2,93x | 2,28x | 3,40x |
Tučný údaj je ten, který si zapamatovat: speculative decoding může generování zpomalit. Při 30 % přijetí s draft, který stojí pětinu target, zaplatíte za pět forward pass a ponecháte 1,4 token. Poslední sloupec je druhá past — delší draft pomáhá jen při vysokém přijetí, protože konec -token odhadu se téměř nikdy nedosáhne. Při 90 % přijetí má hodnotu 3,40x a při 30 % hodnotu 0,79x: stejná konfigurace, výhra nebo prohra podle čísla změřeného na vašem provozu.
Distilace a co nese měkký label
Odkaz na sekci: Distilace a co nese měkký labelKvantizace zmenšuje model uložením stejné funkce do méně bitů. Distilace ho zmenšuje tím, že trénuje menší model, aby napodoboval větší11 — myšlenka, která předchází deep learning téměř o deset let.12
Jemná část je z čeho se student učí. Ne ze správné odpovědi: na té mohl být trénován přímo. Učitel přidává celé rozdělení. Zeptejte se modelu, co následuje po frázi, a podívejte se za argmax:
"She poured the milk into the"
' jug' 0.1355 ' cup' 0.1051 ' bowl' 0.0605 ' large' 0.0380 ' milk' 0.0360Tvrdý label říká jug a nic dalšího. Měkký label říká jug, a také že cup bylo skoro stejně dobré, bowl věrohodné a large — přídavné jméno, úplně jiné gramatické pokračování — stále živé. To je původní argument: tohle je 7, ale vypadá to dost jako 1, a podobnost je informace, kterou tvrdý label zahodí.
Proto také distilace používá teplotu. Vydělení logits hodnotou před softmax zploští rozdělení a zvýší relativní váhu pronásledovatelů: na této frázi spadne poměr mezi top token a třetím z 2,24 při na 1,50 při — druhou odmocninu prvního, což je to, co vydělení logits dvěma udělá s poměrem. Stejné pořadí, více attention ztráty na těsné omyly. Gradient studenta nese nejistotu učitele, ne jen jeho verdikt.
Co se vejde do 8, 16 a 24 GB
Odkaz na sekci: Co se vejde do 8, 16 a 24 GBVšechno v této kapitole je teď jeden součet:
kde je celkový počet rezidentních token napříč všemi souběžnými požadavky. Aplikace: řádky 7B a 70B předpokládají 8 key-value hlav o rozměru 128, řádek 13B plnou multi-head attention se 40 hlavami, jak byly tyto generace modelů stavěné — a je to vidět.
8 GB
| model | přesnost | váhy | volné po režii | token kontextu, které se vejdou |
|---|---|---|---|---|
| 7B | fp16 | 13,0 GB | nevejde se | — |
| 7B | int8 | 6,5 GB | nevejde se | — |
| 7B | int4 (g128) | 3,4 GB | 3,1 GB | 25,710 |
| 13B | int4 (g128) | 6,2 GB | 0,3 GB | 337 |
| 70B | int4 (g128) | 33,6 GB | nevejde se | — |
16 GB
| model | přesnost | váhy | volné po režii | token kontextu, které se vejdou |
|---|---|---|---|---|
| 7B | fp16 | 13,0 GB | 1,5 GB | 11,972 |
| 7B | int8 | 6,5 GB | 8,0 GB | 65,378 |
| 7B | int4 (g128) | 3,4 GB | 11,1 GB | 91,246 |
| 13B | int8 | 12,1 GB | 2,4 GB | 3,136 |
| 13B | int4 (g128) | 6,2 GB | 8,3 GB | 10,822 |
24 GB
| model | přesnost | váhy | volné po režii | token kontextu, které se vejdou |
|---|---|---|---|---|
| 7B | fp16 | 13,0 GB | 9,5 GB | 77,508 |
| 7B | int8 | 6,5 GB | 16,0 GB | 130,914 |
| 7B | int4 (g128) | 3,4 GB | 19,1 GB | 156,782 |
| 13B | int8 | 12,1 GB | 10,4 GB | 13,622 |
| 13B | int4 (g128) | 6,2 GB | 16,3 GB | 21,308 |
| 70B | int4 (g128) | 33,6 GB | nevejde se | — |
Podívejte se na řádek 13B v 8GB tabulce. Váhy se vejdou — 6,2 GB z 8 — takže běžným způsobem řeči 13B model „běží na 8GB kartě“. Má 337 token kontextu, což není konverzace, ale sotva prompt. „Vejde se to“ je špatná otázka. Správná zní „s kolika kontextem a pro kolik uživatelů najednou“.
Podívejte se také na dva řádky int8 v 16GB tabulce. 7B dostane 65 378 token a 13B dostane 3 136 — dvacetinásobný rozdíl z 5,6 GB vah navíc, protože zdejší 13B má multi-head attention a jeho cache stojí 800 KB na token proti 128 KB u 7B. Dva modely podobné velikosti, jeden nepoužitelný pro dlouhý kontext, z důvodu, který se neobjeví v titulku žádné model card.
Kam to pokračuje
Odkaz na sekci: Kam to pokračujePřed třinácti kapitolami to byl perceptron se dvěma vahami a bias. Teď je to transformer, který byl navržen, natrénován, zarovnán, naučen utrácet výpočet na těžké otázky a servírován za změřený náklad na token — bez jediné krabice, která by v něm zůstala neotevřená.
Tady to končí, a končí to záměrně.
Kapitola 14 začíná s modelem někde jinde. Ne ve vašem procesu, ne ve vaší paměti, ne v proměnné, kterou můžete vytisknout: na stroji, který nespravujete, za API klíčem, portem a účtem. Všechno měřené zde se stále děje — prefill stále běží před prvním token, cache stále roste s konverzací, batch, ve kterém jste, stále patří někomu jinému a stále rozhoduje o vaší latenci — ale odteď to pozorujete přes stream Server-Sent Events, finish_reason a HTTP 429 s hlavičkou Retry-After. Otázky se mění s pozorovacím bodem: ne jak se počítá tento gradient, ale proč se mi ztrojnásobila faktura. Mění se i jazyk a kapitola 14 toto pravidlo vysvětluje místo toho, aby ho oznámila — až sem kód držel váhy, gradienty, logits a byty tokenizer; odtud dál drží spojení, retry, zrušení a nahromaděný stav. Třináct kapitol za vámi se přechodem nezahazuje. Jsou popisem toho, co běží na druhé straně portu.
Zdroje a metoda
Odkaz na sekci: Zdroje a metodaDvě vynechávky jsou záměrné. FlashAttention (Dao et al., arXiv:2205.14135) není jiná attention — počítá stejnou funkci dlaždicováním operace tak, aby se matice skóre nikdy nezapisovala do paměti, proto je 67 MB ve druhé tabulce této kapitoly v praxi menší, než aritmetika naznačuje. A samotné kernels jsou delegované: přednáška 10 Stanford CS336 pokrývá inference systémy do hloubky, o kterou se toto nepokouší, a repozitář llama.cpp a specifikace GGUF jsou primární zdroje pro CPU stranu.
Reference
Odkaz na sekci: Reference-
Shazeer, N. Fast Transformer Decoding: One Write-Head is All You Need. arXiv:1911.02150 (2019). Článek je z velké části argument o šířce pásma paměti a tak se i čte. ↩
-
Ainslie, J. et al. GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints. arXiv:2305.13245 (2023). Obsahuje recept na uptraining, který převádí existující multi-head checkpoint, což je důvod, proč se GQA rozšířilo tak rychle. ↩
-
Yu, G.-I., Jeong, J. S., Kim, G.-W., Kim, S. and Chun, B.-G. Orca: A Distributed Serving System for Transformer-Based Generative Models. OSDI 2022. Zavádí plánování na úrovni iterací — continuous batching — a selektivní batching. ↩
-
Kwon, W. et al. Efficient Memory Management for Large Language Model Serving with PagedAttention. arXiv:2309.06180 (2023), SOSP 2023. Článek, na kterém stojí vLLM; §3 je úplná analogie s operačními systémy. ↩
-
Dettmers, T., Pagnoni, A., Holtzman, A. and Zettlemoyer, L. QLoRA: Efficient Finetuning of Quantized LLMs. arXiv:2305.14314 (2023). NF4 je definováno v §3; šestnáct hodnot úrovní použitých ve výše uvedeném měření jsou ty, které tento článek odvozuje. ↩
-
Dettmers, T., Lewis, M., Belkada, Y. and Zettlemoyer, L. LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale. arXiv:2208.07339 (2022). Analýza outlier-feature v §4 je zdrojem jevu měřeného výše, včetně zjištění, že outliers vznikají systematicky ve velkém měřítku. ↩ ↩2
-
Xiao, G., Lin, J., Seznec, M., Wu, H., Demouth, J. and Han, S. SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models. arXiv:2211.10438 (2022). ↩
-
Frantar, E., Ashkboos, S., Hoefler, T. and Alistarh, D. GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers. arXiv:2210.17323 (2022). ↩
-
Lin, J. et al. AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration. arXiv:2306.00978 (2023). ↩
-
Leviathan, Y., Kalman, M. and Matias, Y. Fast Inference from Transformers via Speculative Decoding. arXiv:2211.17192 (2022). Věta 1 je důkaz, že výstupní rozdělení se nemění; Chen et al. (arXiv:2302.01318) publikovali stejnou myšlenku nezávisle. ↩
-
Hinton, G., Vinyals, O. and Dean, J. Distilling the Knowledge in a Neural Network. arXiv:1503.02531 (2015). Teplota a argument „dark knowledge“. ↩
-
Buciluă, C., Caruana, R. and Niculescu-Mizil, A. Model Compression. KDD 2006. Distilace o devět let dříve, pro ensembles místo transformers. ↩