Koliko VRAM-a zapravo troši kontekst na lokalnom modelu?
Jedna od zbunjujućih postavki na alatima za korištenje lokalnih modela je veličina konteksta, koja jede bitno više memorije nego bismo pomislili i čije stvarno zauzeće VRAM-a ne možemo „intuitivno“ predvidjeti jer ovisi o tipu i arhitekturi modela i mora se izračunati
Prozori konteksta od 128 tisuća, 256 tisuća pa i milijun tokena postali su uobičajena stavka na specifikacijama novih jezičnih modela. Na papiru to zvuči kao poziv da u model ubacimo čitave knjige, baze koda ili mjesece prepiske. U praksi, čim takav model pokušamo pokrenuti lokalno, naletimo na prepreku: dostupnu memoriju.
Iako nam je prva briga kod biranja lokalnog modela obično hoće li sam model stati u memoriju grafičke kartice, druga prepreka, veličina konteksta, zna pojesti i više VRAM-a od samog modela. Ova memorija se zove KV cache (key-value cache), a to je memorija u koju se sprema kontekst razgovora. Što je prozor konteksta dulji, to više memorije treba, i to posve neovisno o tome koliko model ima parametara.
Zašto kontekst troši memoriju?
Tijekom generiranja teksta model za svaki već pročitani token pamti dva vektora: ključ (key) i vrijednost (value). Ti se vektori spremaju u takozvani KV cache kako ih model ne bi morao iznova računati pri svakom sljedećem tokenu, što bi inače generiranje usporilo do neupotrebljivosti. Cijena te uštede u brzini plaća se memorijom i to memorijom koja raste linearno: dvostruko dulji kontekst znači dvostruko veći cache.
Kažemo da veličinu ovog cachea nije moguće „intuitivno“ pogoditi jer ne ovisi o samo broju parametara modela, nego o njegovoj arhitekturi. U to su uključeni: broj slojeva modela, broj KV-glava i dimenzija svake glave. Formula izračuna je ova:
memorija po tokenu = 2 x slojevi x KV-glave x dimenzija glave x bajtova po elementu
Faktor dva dolazi otuda što se pamte i ključ i vrijednost.
Uzmimo za primjer Llamu 3 8B: ima 32 sloja, osam KV-glava, dimenziju glave 128 i pamti cache u fp16 formatu (dva bajta). To je 2 x 32 x 8 x 128 x 2, odnosno 131.072 bajta, točno 128 kilobajta po jednom tokenu. Pomnožimo li to sa 128 tisuća tokena, dobivamo oko 16 gigabajta, a s milijun tokena to je – zagrc! - 131 gigabajt. Sve to samo za cache, prije nego što smo u memoriju uopće smjestili sam model.

Zašto dva slična modela troše različito
Ovdje na scenu stupa GQA (grouped-query attention), trik kojim noviji modeli dramatično smanjuju cache. Umjesto da svaka glava pažnje ima vlastiti par ključ-vrijednost, više glava pažnje dijeli isti par. Manje pohranjenih parova znači manji cache, a kvaliteta odgovora pritom gotovo i ne trpi. Posljedica je očita u brojkama: Qwen2.5 7B troši svega 56 KB po tokenu, dok Mistral 7B, praktički iste veličine, troši 128 KB, više nego dvostruko. Drugim riječima, kad birate model za rad s dugim kontekstom, broj parametara govori vam malo; arhitektura govori sve. Dva modela koja na disku zauzimaju jednako mjesta mogu se na duljem kontekstu razlikovati u potrošnji memorije za faktor dva ili više.
KV cache pri fp16 (dva bajta po elementu)
| Model | Po tokenu | 64K | 128K | 256K | 512K | 1M |
| Llama 3.x 8B | 128 KB | 8,59 GB | 17,2 GB | 34,4 GB | 68,7 GB | 131,1 GB |
| Qwen2.5 7B | 56 KB | 3,76 GB | 7,52 GB | 15,0 GB | 30,1 GB | 57,3 GB |
| Mistral 7B | 128 KB | 8,59 GB | 17,2 GB | 34,4 GB | 68,7 GB | 131,1 GB |
| Gemma 2 9B | 336 KB | 22,5 GB | 45,1 GB | 90,2 GB | 180,4 GB | 344,1 GB |
| Llama 3.x 70B | 320 KB | 21,5 GB | 42,9 GB | 85,9 GB | 171,8 GB | 327,7 GB |
| Qwen2.5 32B | 256 KB | 17,2 GB | 34,4 GB | 68,7 GB | 137,4 GB | 262,1 GB |
| Mistral Small 24B | 160 KB | 10,7 GB | 21,5 GB | 42,9 GB | 85,9 GB | 163,8 GB |
* (Navedene brojke su teoretski KV cache izračunat iz formule. Stvarna potrošnja u llama.cpp i Ollami zna biti par posto veća zbog dodatnog troška implementacije i poravnanja memorije - “paddinga”.)
Brojke u tablici odnose se isključivo na KV cache i dolaze povrh težine samog modela. Konkretno: Llama 3 70B u Q4 kvantizaciji zauzima oko 40 GB samo za model; dodamo li 64 tisuće tokena konteksta, treba nam još 21 GB, dakle ukupno preko 60 GB VRAM-a. Prozor od milijun tokena kod tog modela tražio bi 328 GB samo za cache, više nego što imaju četiri vrhunske profesionalne kartice zajedno. Ondje gdje brojke počnu rasti u stotine gigabajta, priča o lokalnom pokretanju polako prelazi u područje radnih stanica i poslužitelja.

Dugi kontekst je i sporiji
Vrijedi spomenuti i dodatne probleme. Prije nego što model ispiše prvi token odgovora, model mora pročitati i obraditi cijeli ulazni kontekst, korak koji se zove prefill. Taj posao raste s duljinom ulaza, pa ćemo kod vrlo dugih razgovora primijetiti osjetnu pauzu prije nego što odgovor uopće krene. Dugi kontekst, dakle, ne košta samo memoriju nego i vrijeme, što je još jedan razlog da prozor ne dižemo na maksimum bez stvarne potrebe.
Kako stisnuti cache: kvantizacija
Dobra je vijest da se i sam cache može kvantizirati. Standardno se ključevi i vrijednosti pamte u fp16 formatu (dva bajta po elementu), no spuštanjem na q8 (jedan bajt) potrošnja se prepolovi, a gubitak kvalitete u praksi je zanemariv. Agresivniji q4 dijeli je na četvrtinu, uz nešto primjetniji, ali i dalje često prihvatljiv kompromis. U llama.cpp to uključujemo prekidačima --cache-type-k i --cache-type-v, a u Ollami varijablom okruženja OLLAMA_KV_CACHE_TYPE. Kvantizacija cachea često je najjeftiniji način da u istu količinu memorije stane dvostruko dulji kontekst.
KV cache pri q8 kvantizaciji cachea (jedan bajt po elementu, upola manje)
| Model | Po tokenu | 64K | 128K | 256K | 512K | 1M |
| Llama 3.x 8B | 64 KB | 4,29 GB | 8,59 GB | 17,2 GB | 34,4 GB | 65,5 GB |
| Qwen2.5 7B | 28 KB | 1,88 GB | 3,76 GB | 7,52 GB | 15,0 GB | 28,7 GB |
| Mistral 7B | 64 KB | 4,29 GB | 8,59 GB | 17,2 GB | 34,4 GB | 65,5 GB |
| Gemma 2 9B | 168 KB | 11,3 GB | 22,5 GB | 45,1 GB | 90,2 GB | 172,0 GB |
| Llama 3.x 70B | 160 KB | 10,7 GB | 21,5 GB | 42,9 GB | 85,9 GB | 163,8 GB |
| Qwen2.5 32B | 128 KB | 8,59 GB | 17,2 GB | 34,4 GB | 68,7 GB | 131,1 GB |
| Mistral Small 24B | 80 KB | 5,37 GB | 10,7 GB | 21,5 GB | 42,9 GB | 81,9 GB |
Što to znači u praksi
Pogledamo li brojke trezveno, kontekst od milijun tokena na lokalnom hardveru i dalje je luksuz rezerviran za radne stanice. Čak i skroman model od sedam milijardi parametara traži oko 57 GB samo za cache na fp16, više nego što ima ijedna potrošačka grafička kartica. Tek uz kvantizirani cache i prebacivanje dijela tereta na sistemsku memoriju takvi prozori postaju izvedivi, i to uz osjetan pad brzine.
Za svakodnevni rad, sažimanje dokumenata, programiranje ili pretragu nad lokalnom bazom znanja, prozor od 32 do 128 tisuća tokena posve je dovoljan, a potrošnja ostaje u razumnim granicama. Vrijedi i obrnuto pravilo: ako vam je kontekst kratak, slobodno birate veći i pametniji model jer vam cache neće pojesti memoriju.
Zaključak
Iako je lakše komunicirati s modelima s većim kontekstom, prije nego u konfiguraciji podignemo prozor na maksimum, valja izračunati koliko to košta u zauzeću VRAM-a, jer KV cache zna nečujno pojesti memoriju za koju smo mislili da u njoj imamo učitani model. Pravilo palca je da provjerimo arhitekturu modela, ne samo broj parametara, i postavimo prozor konteksta na onoliko koliko nam stvarno treba. Kad god možemo trebali bismo koristiti kvantizaciju cachea.
