AI-gidsen · 10 min lezen

Wat is een KV cache? Hoe LLM's aandachtsstatussen hergebruiken

Ontdek wat een KV cache is, hoe die LLM-decodering versnelt, met de contextlengte groeit en samenhangt met promptcaching en API-kosten.

Illustratie van een reeks gekleurde tokentegels die gestapelde cacheblokken vullen, waaruit een snellere lichtgevende uitvoerstroom komt

Een KV cache (Key-Value-cache) is een optimalisatie voor inferentie waarbij een groot taalmodel de per laag berekende Key- en Value-attentietensoren voor reeds verwerkte tokens opslaat. Tijdens autoregressieve decodering hergebruikt het model deze opgeslagen toestanden in plaats van bij elke stap alle eerdere tokens opnieuw te berekenen. Zo neemt overbodig rekenwerk af naarmate reeksen groeien.

Of je nu gehoste API’s aanroept of modellen met open gewichten op eigen GPU’s draait, de KV cache speelt een centrale rol bij latentie, geheugendruk en doorvoer. Inzicht in de werking verklaart waarom de tijd tot het eerste token anders aanvoelt dan de streamingsnelheid, waarom lange contexten zoveel VRAM verbruiken en waarom promptcaching tussen API-verzoeken exact overeenkomende prefixen vereist.

Hoe werkt attentie zonder KV cache?

Zonder KV cache moet een autoregressieve Transformer telkens wanneer hij één nieuw token in een AI-model genereert, de attentieprojecties voor de hele reeks vanaf het begin opnieuw berekenen.

In elke decoderlaag van de Transformer projecteert zelfattentie elke representatie van een invoertoken naar drie vectoren:

  • Query ($Q$): vertegenwoordigt de huidige positie die naar relevante context zoekt.
  • Key ($K$): vertegenwoordigt wat elke positie biedt om met Query’s te vergelijken.
  • Value ($V$): bevat de informatie die wordt samengevoegd nadat attentiegewichten zijn berekend uit de inproducten van Query en Key.

Omdat taalmodellen met alleen een decoder tekst één token tegelijk genereren, hoeft bij het genereren van token $t+1$ alleen de Query-vector van het nieuwste token aandacht te richten op de Keys en Values van tokens $1$ tot en met $t$. Als de runtime die Keys en Values na het uitsturen van token $t$ weggooit, moet stap $t+1$ elk prefixtoken opnieuw door elke laag sturen om precies dezelfde $K$- en $V$-tensoren te reconstrueren. Naarmate de prompt en uitvoer langer worden, groeit dat herhaalde werk kwadratisch over de volledige generatie.

Waarom maakt causale attentie KV-caching mogelijk?

Causale maskering garandeert dat de Key- en Value-tensoren van een token alleen afhangen van eerdere tokens, nooit van tokens die later worden gegenereerd.

In een causale decoder (van links naar rechts) kan token $i$ geen aandacht richten op een positie $j > i$. Omdat informatie alleen vooruit door de reeks stroomt:

  1. Ligt de representatie van token $i$ op laag $l$ volledig vast zodra tokens $1 \dots i$ zijn verwerkt.
  2. Verandert het toevoegen van token $i+1$ aan het einde van de reeks in geen enkele laag de verborgen toestanden, Keys of Values van posities $1 \dots i$.
  3. Kan de runtime de nieuw berekende vectoren $K_{i+1}$ en $V_{i+1}$ toevoegen aan een buffer per laag, de KV cache, en alleen het nieuwste token doorgeven aan de volgende decoderingsstap.

Zoals beschreven in de Hugging Face Transformers-gids voor KV cache en het overzicht van de Transformers-cachearchitectuur, verandert caching elke decoderingsstap van een verwerking van de volledige reeks in een incrementele update van één token, die eerdere Keys en Values uit het geheugen leest.

Hoe gebruiken Prefill en Decode de KV cache verschillend?

LLM-inferentie bestaat uit twee afzonderlijke fasen, Prefill en Decode, die op fundamenteel verschillende manieren met de KV cache omgaan en tegen verschillende hardwareknelpunten aanlopen.

Inferentiefase Wat gebeurt er met de KV cache? Belangrijkste hardwareknelpunt Voor gebruiker zichtbare maatstaf
Prefill (promptverwerking) Berekent $K$ en $V$ voor alle prompttokens parallel en vult de initiële cache in elke laag. Beperkt door rekenkracht (grote matrixvermenigvuldigingen over de volledige prompt). Tijd tot het eerste token (TTFT).
Decode (tokengeneratie) Berekent $Q$, $K$ en $V$ voor telkens één nieuw token, voegt de nieuwe $K$/$V$-invoer toe en leest de volledige cachegeschiedenis. Beperkt door geheugenbandbreedte (laden van modelgewichten en groeiende KV-toestanden bij elke stap). Latentie tussen tokens / streamingsnelheid van uitvoer.

Zoals uitgelegd in NVIDIA’s technische uitleg over Prefill, Decode en KV-caching, verwerkt de Prefill-fase alle invoertokens tegelijk. Omdat de volledige prompt vooraf bekend is, kunnen GPU’s de projecties bundelen in dichte matrixbewerkingen, rekeneenheden volledig benutten en de KV cache in één doorgang vullen. Lange prompts, zoals contexten met meerdere documenten in retrieval-augmented generation-pijplijnen (RAG), verhogen direct het Prefill-werk en de tijd tot het eerste token.

Zodra het eerste uitvoertoken is uitgegeven, begint de Decode-fase. Elke stap verwerkt slechts één nieuw token per reeks, voegt de Key- en Value-vectoren ervan toe aan de cache en leest alle eerder opgeslagen $K$- en $V$-tensoren uit GPU-geheugen met hoge bandbreedte (HBM) om attentie te berekenen. Omdat de rekenintensiteit per stap laag is en het aantal geheugenlezingen met de reekslengte en batchgrootte groeit, wordt decodering al snel beperkt door geheugenbandbreedte en beschikbaar VRAM in plaats van ruwe reken-FLOPs.

Hoeveel geheugen gebruikt een KV cache?

Het geheugengebruik van een KV cache groeit lineair met de reekslengte, batchgrootte, het aantal lagen en het aantal Key-Value-attentieheads.

Uit de tensorvormen in standaard Transformer-caches, waarbij elke laag één Key-tensor en één Value-tensor opslaat over batch, KV-heads, reekslengte en headdimensie, kan de cachegrootte per reeks symbolisch worden uitgedrukt als:

bytes ≈ 2 × layers × kv_heads × head_dim × seq_len × bytes_per_element

Elke term in die formule is een factor waarmee modelarchitecten en inferentie-engineers het geheugengebruik beheersen:

  • 2: staat voor de opslag van zowel Key- (K) als Value-tensoren (V).
  • layers: totaal aantal Transformer-decoderblokken in het model.
  • kv_heads en head_dim: het aantal Key/Value-attentieheads per laag en de dimensie van elke head.
  • seq_len: totaal aantal gecachte tokens (prompttokens plus tot nu toe gegenereerde tokens).
  • bytes_per_element: numerieke precisie van de opgeslagen tensoren (bijvoorbeeld 2 bytes voor FP16/BF16, 1 byte voor 8-bitsformaten of een kleinere fractie bij 4-bits KV cache-kwantisatie zoals NVFP4).

kv_heads verkleinen: MHA, MQA en GQA

Bij klassieke Multi-Head Attention (MHA) heeft elke Query-head een eigen Key-head en Value-head (kv_heads = query_heads). Toen contextvensters en batchgroottes groeiden, werd het opslaan van afzonderlijke K- en V-projecties voor elke head een belangrijk geheugenknelpunt.

Twee architectuurwijzigingen verminderen kv_heads in de bovenstaande formule direct:

Naast GQA verlagen inferentie-engines ook bytes_per_element door gecachte K- en V-tensoren te kwantiseren naar formaten met een lagere precisie. Daardoor passen langere contexten of grotere gelijktijdige batches in GPU-geheugen.

Hoe beheren serving-engines KV cache-geheugen op grote schaal?

Serving-engines in productie beheren KV cache-geheugen door aaneengesloten tensoren in blokken van vaste grootte te splitsen, zodat GPU’s meer gelijktijdige verzoeken kunnen verwerken zonder VRAM te verspillen.

In naïeve implementaties reserveert een serving-runtime vooraf een aaneengesloten stuk GPU-geheugen ter grootte van de maximaal mogelijke reekslengte van elk verzoek. Omdat de werkelijke uitvoerlengte vooraf niet bekend is, veroorzaakt deze reservering ernstige interne en externe geheugenfragmentatie: gereserveerde plaatsen blijven ongebruikt terwijl nieuwe verzoeken niet kunnen worden uitgevoerd.

Kwon et al. pakten dit knelpunt aan met PagedAttention in Efficient Memory Management for Large Language Model Serving with PagedAttention (arXiv:2309.06180), het kernmechanisme achter vLLM. Het is geïnspireerd op virtueel geheugen en paging van besturingssystemen:

  • Niet-aaneengesloten blokken van vaste grootte: PagedAttention verdeelt de KV cache van elk verzoek in blokken van vaste grootte met Keys en Values voor een vast aantal tokens, gekoppeld via een tabel van logische naar fysieke blokken.
  • Toewijzing op aanvraag: nieuwe fysieke blokken worden pas toegewezen wanneer nieuwe tokens worden gegenereerd, waardoor interne fragmentatie vrijwel verdwijnt.
  • Prefixdeling op blokniveau: wanneer meerdere verzoeken een identieke promptprefix delen, zoals een systeemprompt of gedeelde few-shot-voorbeelden, kunnen hun logische bloktabellen naar dezelfde fysieke KV-blokken in het geheugen verwijzen. Copy-on-write wordt pas gebruikt wanneer reeksen uiteenlopen.

In de evaluatie van het vLLM-paper (arXiv:2309.06180) bereikte dit paginagebaseerde KV-beheer bij vergelijkbare latentie een 2–4× hogere doorvoer dan FasterTransformer en Orca, doordat aanzienlijk grotere batches in hetzelfde GPU-geheugen pasten. Moderne serving-stacks, waaronder vLLM en NVIDIA TensorRT-LLM’s systeem voor hergebruik van KV cache, gebruiken blokgebaseerd KV-beheer om gedeelde prefixen te hergebruiken en gelijktijdigheid te maximaliseren.

Wat is het verschil tussen een KV cache en promptcaching?

Een KV cache is het onderliggende inferentiemechanisme per verzoek dat attentietoestanden tussen decoderingsstappen hergebruikt. Promptcaching is een serving- en API-functie over meerdere verzoeken heen die die KV-blokken bewaart en hergebruikt tussen afzonderlijke aanroepen met een identieke prefix.

Omdat ontwikkelaars beide termen vaak tegenkomen bij het optimaliseren van latentie en kosten, helpt het om te onderscheiden wat binnen één generatieaanroep gebeurt en wat tussen meerdere API-verzoeken gebeurt:

Aspect KV cache (decodering binnen een verzoek) Promptcaching (prefixhergebruik tussen verzoeken)
Bereik Eén verzoek, over tokengeneratiestappen heen. Meerdere verzoeken binnen een korte bewaarperiode.
Wat wordt vermeden Eerdere tokens binnen hetzelfde antwoord opnieuw berekenen bij het decoderen van elk nieuw token. De Prefill opnieuw uitvoeren op herhaalde statische promptprefixen tussen afzonderlijke aanroepen.
Levenscyclus Aangemaakt tijdens Prefill, uitgebreid tijdens Decode en vrijgegeven of verwijderd wanneer het verzoek is voltooid. Voor toekomstige aanroepen bewaard in blokopslag (GPU- of hostgeheugen), met een verwijderingsbeleid en TTL.
Zichtbaarheid op de rekening Onderdeel van standaardinferentie; geen afzonderlijke factuurregel. Vaak door API-aanbieders aangeboden als korting op gecachte invoertokens en lagere TTFT.

Causale attentie bepaalt een strikte regel voor promptcaching: de prefix moet vanaf het allereerste token exact overeenkomen. Omdat de Key- en Value-tensoren van elk token afhangen van de exacte reeks voorgaande tokens, maakt zelfs een wijziging van één tijdstempel, gebruikers-ID of instructie bovenaan een prompt de gecachte $K$- en $V$-toestanden van alle volgende tokens ongeldig.

Raadpleeg voor bewaartermijnen, minimale tokendrempels en kortingen per aanbieder onze volledige gids over wat promptcaching is en hoe aanbieders die prijzen.

Praktische aandachtspunten voor ontwikkelaars en teams

Inzicht in het gedrag van de KV cache leidt direct tot een betere promptarchitectuur, stabielere latentie en lagere infrastructuurkosten:

  1. Zet statische inhoud helemaal aan het begin van je prompt: plaats systeeminstructies, toolschema’s en gedeelde referentiedocumenten vóór dynamische gebruikersinvoer of metadata per verzoek. Een identieke prefix laat zowel gehoste API’s als lokale engines zoals vLLM of TensorRT-LLM gecachte KV-blokken hergebruiken in plaats van overbodige Prefill-bewerkingen uit te voeren.
  2. Let op de invloed van contextlengte op gelijktijdigheid bij zelf gehoste GPU’s: omdat de KV cache lineair met seq_len groeit, halveert een verdubbeling van de gemiddelde contextlengte het aantal gelijktijdige verzoeken dat in GPU-geheugen past. Kies bij zelf gehoste modellen met open gewichten bij voorkeur architecturen met Grouped-Query Attention (GQA) en runtimes die paginagebaseerde KV-toewijzing en KV-kwantisatie ondersteunen.
  3. Schrap overbodige context, ook bij grote contextvensters: grote geschiedenissen bij elke beurt invoeren verhoogt de Prefill-latentie, KV-geheugendruk en tokenkosten. Combineer prefixcaching met gerichte retrieval en periodieke samenvattingen: kerntactieken in onze gids over AI API-kosten verlagen en onze AI API-prijsvergelijking.

Veelgestelde vragen

Verandert een KV cache de uitvoerkwaliteit van het model?

Een standaard KV cache in de eigen inferentieprecisie van het model, zoals FP16 of BF16, is wiskundig gelijkwaardig aan het opnieuw berekenen van de prefix bij elke stap, omdat causale maskering voorkomt dat toekomstige tokens eerdere Key- en Value-vectoren wijzigen. Kwaliteitsafwegingen ontstaan pas bij verliesgevende KV cache-kwantisatie, zoals 8-bits- of 4-bitscompressie, of het verwijderen van oudere tokens om geheugen te besparen.

Waarom kan een KV cache tokens midden in een prompt niet hergebruiken als het begin is veranderd?

In een causale Transformer omvat de representatie van elk token attentie over alle voorgaande tokens in de reeks. Als de eerste tien tokens van een prompt veranderen, veranderen ook de verborgen toestanden en daarmee de Key- en Value-projecties vanaf token elf. Omdat die toestanden afhangen van de exacte voorafgaande prefix, kunnen serving-engines gecachte KV-blokken tussen verzoeken alleen hergebruiken wanneer de prefix vanaf het begin overeenkomt.

Bevat de KV cache de modelgewichten?

Nee. Modelgewichten zijn statische parameters die tijdens training zijn geleerd en worden gedeeld door alle verzoeken die de GPU verwerkt. De KV cache bevat dynamische, verzoekspecifieke activatietensoren (de Key- en Value-projecties van de tokens die momenteel in de context staan). Het totale GPU-geheugen moet tijdens inferentie ruimte bieden aan de statische modelgewichten, tijdelijke runtime-activaties en de dynamische KV caches voor alle actieve verzoeken in de batch.

Waarom vertraagt het genereren van meer uitvoertokens bij hoge gelijktijdigheid?

Tijdens de Decode-fase moeten bij elke nieuwe tokenstap de opgeslagen Key- en Value-tensoren voor alle eerdere tokens in elk actief verzoek in de batch worden gelezen. Naarmate reeksen langer worden en het aantal gelijktijdige verzoeken stijgt, neemt de hoeveelheid KV-data toe die per stap uit GPU-geheugen wordt overgedragen. Dit verzadigt de geheugenbandbreedte en dwingt de scheduler de batchgrootte te beperken of op geheugenlezingen te wachten.

Bronnen

Geschreven door

Chunyu Cai

Chunyu Cai is CEO van Lotook, LLC en ondernemer. Hij schrijft over AI-modellen, agenten en hoe ze het dagelijkse werk veranderen. Chunyu Cai heeft een masterdiploma van Imperial College London.