Kämpar du med AI eller fullstack-utveckling? Våra experter finns här för att vägleda dig: skräddarsydda råd, teknisk integration och mycket mer. Nå ut till oss på [email protected].

Tekniker för optimering av LLM-inferens

Inferensoptimering är en kritisk del av generativa AI-applikationer som används i produktion. Att använda LLM:er effektivt i stor skala är en utmaning och många tekniker har utvecklats under de senaste åren för att göra inferens snabbare och billigare. Låt oss granska dessa tekniker i den här artikeln.

Tekniker för optimering av LLM-inferens

Fokus på LLM:s arkitektur

Stora språkmodeller (LLM) är alla baserade på transformatorarkitekturen som uppfanns 2017 av Vaswani et al. Transformatorarkitekturen uppnår överlägsen noggrannhet, få-shot-inlärning och nära mänskliga förmågor över olika språkuppgifter. Dessa grundmodeller, som ofta består av tiotals till hundratals miljarder parametrar, är dock kostsamma att träna och resurskrävande under inferens. Inferenskostnaderna eskalerar med långa inmatningskontexter, som kräver betydande processorkraft på grund av stora indata. Detta gör effektiv inferens till en kritisk utmaning, särskilt när det gäller att hantera minnes- och beräkningsresurser.

Transformatorns arkitektur
Transformatorns arkitektur

Mer specifikt är de mest välkända LLM-modellerna LLM-modeller som endast använder avkodare, som GPT-3, GPT-4, LLaMA, Mistral, DeepSeek osv. Dessa modeller är förtränade på en kausal modelleringsuppgift och fungerar som förutsägare för nästa ord. De bearbetar en sekvens av tokens som indata och producerar följande tokens autoregressivt tills ett stoppvillkor uppnås.

LLM-inferens i modeller med enbart avkodare innefattar två viktiga faser: prefill-fasen och avkodningsfasen. I prefill-fasen bearbetar modellen inmatade tokens för att beräkna mellanliggande tillstånd (nycklar och värden) för att generera den första nya token. Denna fas, som liknar en matris-matrisoperation, är mycket parallelliserad och utnyttjar effektivt GPU-kapacitet. Omvänt genererar avkodningsfasen tokens, en i taget, med hjälp av tidigare tokens tillstånd. Denna matris-vektoroperation är minnesbunden, eftersom dataöverföringen till GPU:n, snarare än beräkningshastigheten, i första hand dikterar latensen, vilket leder till underutnyttjad GPU-beräkningskraft.

Optimering av avkodningsfasen är en central punkt för att hantera inferensutmaningar. Lösningarna inkluderar utveckling av effektiva uppmärksamhetsmekanismer och bättre hantering av nycklar och värden för att minska flaskhalsar i minnet. Inlägget belyser praktiska tillvägagångssätt för att förbättra inferensprestandan, förutsatt att läsarna har en grundläggande förståelse för transformatorarkitekturen och uppmärksamhetsmekanismerna. Dessa optimeringar är avgörande för att förbättra genomströmningen och minska latensen i verkliga LLM-distributioner.

En ytterligare komplikation uppstår genom att olika tokenizers används i olika LLM:er, vilket påverkar jämförbarheten mellan token. Tokens, som ungefär motsvarar fyra engelska tecken, varierar i representation beroende på tokenizer, vilket gör direkta jämförelser av inferensgenomströmning (t.ex. tokens per sekund) missvisande. Denna variation understryker behovet av standardiserade utvärderingsmått för att korrekt bedöma och jämföra LLM-prestanda under inferens.

Batchning

Batchning är en viktig strategi för att förbättra GPU-användningen och genomströmningen i stora språkmodeller (LLM). Genom att bearbeta flera förfrågningar samtidigt med samma modell fördelar batching minneskostnaden för modellvikter över förfrågningar, vilket gör att större batcher kan utnyttja mer GPU-beräkningskraft. Det finns dock en gräns för batchstorleken, eftersom alltför stora batcher kan orsaka minnesöverflöd på grund av LLM:ernas minneskrav, särskilt i samband med KV-cachelagring (key-value) (mer om detta senare).

Batchningstekniker
Batchningstekniker

Traditionell eller statisk batchning har begränsningar eftersom förfrågningar inom en batch ofta genererar olika antal completion tokens, vilket leder till varierande exekveringstider. Detta gör att alla begäranden måste vänta på att den långsammaste ska slutföras, vilket kan vara problematiskt när genereringslängderna varierar avsevärt. För att lösa detta har avancerade tekniker som batchning under flygning utvecklats för att optimera prestandan.

In-flight batching, även kallat kontinuerlig batching, hanterar de utmaningar som uppstår på grund av LLM-arbetsbelastningarnas dynamiska natur, som kan variera från enkla chatbot-svar till komplex dokumentsammanfattning eller kodgenerering. Dessa uppgifter ger resultat av mycket olika storlek, vilket gör det svårt att batcha och exekvera förfrågningar effektivt parallellt. Till skillnad från statisk batchning gör batchning under flygning det möjligt för servern att omedelbart ta bort slutförda sekvenser från batchen och börja bearbeta nya förfrågningar medan andra fortfarande pågår. Detta tillvägagångssätt ökar GPU-användningen avsevärt genom att anpassa sig till de varierande exekveringstiderna för förfrågningar i verkliga scenarier.

Multi-GPU-distribution med parallellisering av modeller

Modellparallellisering är en kritisk strategi för att hantera minnes- och beräkningskraven för storskaliga maskininlärningsmodeller genom att distribuera dem över flera GPU: er. Detta tillvägagångssätt möjliggör hantering av större modeller eller inmatningsbatcher som överstiger minneskapaciteten för en enda enhet, vilket gör det viktigt för både träning och slutsats när minnesbegränsningar är snäva. Det finns olika tekniker för att dela upp modellvikter, inklusive pipeline-parallellism, tensorparallellism och sekvensparallellism, som var och en hanterar olika aspekter av modelldistribution. Till skillnad från dataparallellism, som fokuserar på att replikera modellvikter mellan enheter för att bearbeta större indatapartier under träning, är dessa metoder mer relevanta för att minska minnesavtrycken under både träning och inferens.

Flera NVIDIA GPU:er
Flera NVIDIA GPU:er

Pipeline-parallellism delar upp modellen vertikalt i sekventiella bitar, där varje bit innehåller en delmängd av lager som tilldelats en separat enhet. I en fyrvägs pipelinekonfiguration hanterar till exempel varje enhet en fjärdedel av modellens lager och skickar utdata till nästa enhet i sekvensen. Även om detta avsevärt minskar minneskraven per enhet, introducerar det ineffektiviteter som kallas "pipelinebubblor", där enheter kan gå på tomgång medan de väntar på utdata från tidigare lager. Microbatching, som delar upp inmatningsbatcher i mindre delbatcher för sekventiell bearbetning, kan minska dessa bubblor men inte eliminera dem helt, eftersom tomgångstider kvarstår under framåt- och bakåtpassager.

Tensorparallellism, däremot, delar upp enskilda lager horisontellt i mindre beräkningsblock som kan köras oberoende av varandra på olika enheter. Detta är särskilt effektivt för transformatorkomponenter som uppmärksamhetsblock och flerlagerperceptroner (MLP), där till exempel olika uppmärksamhetshuvuden kan tilldelas separata enheter för parallell beräkning. Tensorparallellism är dock mindre effektivt för operationer som LayerNorm och Dropout, som inte enkelt kan delas upp och måste replikeras över enheter, vilket leder till överflödig minnesanvändning för lagring av aktiveringar. Denna begränsning belyser behovet av kompletterande metoder för att optimera minneseffektiviteten.

Sekvensparallellism hanterar minnesineffektiviteten i operationer som LayerNorm och Dropout genom att partitionera dem längs indatasekvensdimensionen och utnyttja deras oberoende av sekvenselement. Den här metoden minskar minnesavtrycket från redundanta aktiveringar, vilket gör den till ett värdefullt komplement till tensorparallellism. Dessa parallelliseringstekniker utesluter inte varandra utan kan kombineras för att optimera stora språkmodeller (LLM) ytterligare. Dessutom kan specifika optimeringsstrategier för uppmärksamhetsmodulen förbättra skalbarheten och minska minneskraven per GPU, vilket möjliggör effektivare träning och inferens för stora modeller.

Optimering av uppmärksamhet

I 2017 års artikel *Attention Is All You Need* av Vaswani et al. introducerades Transformer-modellen, med självuppmärksamhet som hörnsten. Självuppmärksamhet gör det möjligt för modellen att bedöma relevansen av olika ord i en mening i förhållande till varandra, vilket förbättrar den kontextuella förståelsen för uppgifter som naturlig språkbehandling. I artikeln formaliseras självuppmärksamhet, särskilt genom SDPA-mekanismen (scaled dot-product attention), som mappar frågor och nyckelvärdepar till en utdata, vilket gör den till en central komponent i moderna neurala nätverk. Här är några av de viktigaste teknikerna för att optimera uppmärksamhetsberäkningar:

Uppmärksamhetspapperet
Uppmärksamhetspapperet

Multi-head attention (MHA) bygger på SDPA genom att köra flera uppmärksamhetsoperationer parallellt, var och en med distinkta projektioner av fråge-, nyckel- och värdematriser. Dessa parallella operationer, eller "huvuden", fokuserar på olika representativa underutrymmen, vilket berikar modellens förståelse av indata. Utdata från dessa huvuden sammankopplas och projiceras linjärt, vilket upprätthåller beräkningseffektivitet som kan jämföras med uppmärksamhet med ett huvud genom att minska dimensionaliteten för varje huvud (t.ex. genom att dela modelldimensionen med antalet huvuden, t.ex. 8).

Multi-query attention (MQA) optimerar MHA för inferens genom att dela nyckel- och värdeprojektioner över flera attention heads samtidigt som flera query-projektioner behålls. Detta minskar kraven på minnesbandbredd och storleken på KV-cachen (key-value), vilket möjliggör större batchstorlekar och bättre beräkningsutnyttjande. MQA kan dock minska noggrannheten något, och modeller som utnyttjar det kräver träning eller finjustering med MQA aktiverat för att bibehålla prestandan.

Grouped-query attention (GQA) balanserar MHA och MQA genom att gruppera frågehuvuden och dela nyckelvärdesprojektioner inom varje grupp, vilket ger en kvalitet som ligger nära MHA med en beräkningseffektivitet som ligger närmare MQA. Modeller som Llama 2 70B använder GQA, och de som tränats med MHA kan anpassas till GQA med minimal ytterligare träning. Både MQA och GQA minskar kraven på KV-cacheminne, även om ytterligare optimeringar av cachehanteringen fortfarande är nödvändiga.

FlashAttention förbättrar uppmärksamhetsmekanismerna genom att omorganisera beräkningar för att utnyttja GPU:ns minneshierarkier mer effektivt. Till skillnad från traditionell lager-för-lager-bearbetning sammanfogar FlashAttention operationer och använder "tiling" för att beräkna små delar av utgångsmatrisen på en gång, vilket minimerar läs- och skrivoperationer i minnet. Denna I/O-medvetna, exakta uppmärksamhetsalgoritm integreras sömlöst i befintliga modeller utan ändringar och ger betydande hastighetsökningar genom att optimera dataförflyttningen.

Cachelagring av nyckelvärden

KV-caching är en kritisk optimeringsteknik som används under avkodningsfasen av stora språkmodeller (LLM) för att förbättra effektiviteten i självuppmärksamhetsberäkningar. I denna fas beror varje genererad token på nyckel- (K) och värdetenorerna (V) för alla tidigare tokens, inklusive de som beräknats under prefill-stadiet och efterföljande avkodningssteg. I stället för att räkna om dessa tensorer för varje token vid varje tidssteg lagras de i GPU-minnet och nya tensorer läggs till i cacheminnet när de beräknas. Vanligtvis upprätthålls en separat KV-cache för varje lager i modellen, vilket avsevärt minskar redundanta beräkningar och påskyndar avkodningsprocessen.

Cachelagring av nyckelvärden
Cachelagring av nyckelvärden

Minneskraven för LLM:er på GPU:er drivs främst av två komponenter: modellvikter och KV-cache. Modellvikter, som består av modellens parametrar, upptar betydande minne; till exempel kräver en 7-miljarders parametermodell som Llama 2 7B i 16-bitars precision cirka 14 GB. KV-cachen lagrar däremot självuppmärksamhetstensorer för att undvika omräkning, och dess storlek bestäms av faktorer som antalet lager, uppmärksamhetshuvuden, huvuddimensioner och precision. För varje token beräknas cachestorleken som 2 * num_layers * (num_heads * dim_head) * precision_in_bytes, där faktorn 2 står för både K- och V-matriser. För en batch av indata skalar den totala KV-cachestorleken med batchstorlek och sekvenslängd och kan potentiellt nå betydande storlekar, t.ex. ~2 GB för en Llama 2 7B-modell med en sekvenslängd på 4 096 och batchstorlek 1.

Att hantera KV-cachen effektivt innebär utmaningar på grund av dess linjära tillväxt med batchstorlek och sekvenslängd, vilket kan begränsa genomströmningen och komplicera hanteringen av inmatningar med långa kontexter. En vanlig ineffektivitet beror på statisk överkapacitet, där minne reserveras för den maximala sekvenslängd som stöds (t.ex. 2 048 tokens), oavsett den faktiska storleken på indata. Detta leder till betydande minnesförluster eller fragmentering, eftersom en stor del av det reserverade utrymmet ofta förblir oanvänt under hela förfrågningens livstid och binder upp värdefulla GPU-minnesresurser.

För att hantera dessa ineffektiviteter introducerar PagedAttention-algoritmen ett nytt tillvägagångssätt som är inspirerat av operativsystemets personsökning. Den delar upp KV-cachen i block av fast storlek, som var och en representerar ett visst antal tokens, som kan lagras i minnet utan att vara sammanhängande. En blocktabell spårar dessa block och hämtar dem efter behov under beräkningar av uppmärksamhet. När nya tokens genereras allokeras ytterligare block dynamiskt. Den här metoden minimerar minnesförlusten genom att eliminera behovet av sammanhängande allokering och överkapacitet, vilket möjliggör större batchstorlekar och förbättrar genomströmningen, vilket gör den till ett betydande framsteg i hanteringen av KV-cacheminne för LLM:er.

Optimering av modeller

I det här avsnittet diskuterar vi olika tekniker för att optimera stora språkmodeller (LLM) för att minska deras minnesförbrukning och förbättra prestanda på GPU:er. Nyckelmetoder inkluderar kvantisering, gleshet och destillation, var och en inriktad på olika aspekter av modelleffektivitet. Dessa tekniker modifierar modellvikter, utnyttjar GPU-hårdvaruacceleration och överför kunskap till mindre modeller, vilket gör att större modeller kan köras på begränsad hårdvara med bibehållen prestanda. Dessa metoder kan försämra modellens noggrannhet, så de bör användas med försiktighet.

Kvantisering minskar precisionen i en modells vikter och aktiveringar, vanligtvis från 32 eller 16 bitar till 8 eller färre bitar, vilket gör att modeller kan uppta mindre minne och överföra data mer effektivt. Medan kvantisering av vikter är okomplicerad på grund av deras fasta natur efter träning, är kvantisering av aktiveringar mer komplex på grund av avvikelser som utökar deras dynamiska intervall. Tekniker som LLM.int8 () hanterar detta genom att selektivt tillämpa högre precision på vissa aktiveringar, eller genom att återanvända det dynamiska intervallet för kvantiserade vikter för aktiveringar, även om GPU: er kan kräva att vikter konverteras tillbaka till högre precision för operationer.

Sparsamhet innebär att modellvärden nära noll beskärs, vilket skapar glesa matriser som kräver mindre minne. GPU:er stöder strukturerad gleshet, till exempel att representera två av fyra värden som nollor, vilket påskyndar beräkningarna. Genom att kombinera gleshet med kvantisering kan exekveringshastigheten förbättras ytterligare. Forskningen fortsätter att utforska optimala glesa representationer för LLM, vilket indikerar en lovande väg för att förbättra inferenshastigheterna.

Genom destillering överförs kunskap från en större "lärarmodell" till en mindre "elevmodell", vilket komprimerar storleken med bibehållen prestanda. DistilBERT uppnår t.ex. en 40-procentig storleksminskning och en 60-procentig hastighetsökning jämfört med BERT, med 97% av dess kapacitet kvar. Destillation kan innebära att man härmar lärarens resultat eller använder lärargenererade data för utbildning, med metoder som "Distilling Step by Step!" som innehåller rationaler för effektiv inlärning. Restriktiva licenser för många avancerade LLM-program begränsar dock tillgången till lämpliga lärarmodeller för destillering.

Spekulativ slutledning

Spekulativ inferens, även känd som spekulativ sampling eller assisterad generering, är en metod för att parallellisera exekveringen av autoregressiva stora språkmodeller (LLM) som GPT-stilmodeller, som vanligtvis genererar text token för token. Vid standardkörning är varje token beroende av alla tidigare tokens för kontext, vilket gör parallellgenerering omöjlig eftersom den n:te token måste genereras före den (n+1)-te. Spekulativ inferens hanterar detta genom att använda en "billigare" utkastmodell för att förutsäga flera framtida tokens samtidigt, som sedan verifieras eller avvisas parallellt av huvudmodellen, vilket möjliggör snabbare textgenerering.

Processen innebär att ett utkast till fortsättning av flera tokens genereras med en mindre resurskrävande metod, följt av en parallell verifiering med huvudmodellen där utkastet används som spekulativt sammanhang. Om verifieringsmodellen matchar utkastet till tokens accepteras de; i annat fall kasseras tokens som inte matchar och efterföljande tokens, och processen upprepas med ett nytt utkast. Utkast till tokens kan genereras på olika sätt, t.ex. genom att träna flera modeller, finjustera flera huvuden på en förtränad modell för att förutsäga framtida tokens, eller använda en mindre utkastmodell tillsammans med en större, mer kapabel verifieringsmodell, var och en med sina egna avvägningar.

Disaggregerad inferens

Disaggregerad inferens är en teknik där beräkningsuppgifterna delas upp på olika hårdvara för att optimera prestanda, kostnad och resursanvändning. Specifikt separeras faserna prefilling och decoding. Genom att dela upp dessa faser kan varje fas tilldelas den hårdvara som är bäst lämpad för dess beräkningskrav, vilket förbättrar effektiviteten och skalbarheten.

Disaggregerad inferens
Disaggregerad inferens

Förfyllning är beräkningsintensivt och kräver betydande matrismultiplikationer för att bearbeta hela inmatningsuppmaningen och producera KV-cacher. Denna fas drar nytta av högpresterande hårdvara som GPU:er eller TPU:er, som utmärker sig för parallella beräkningar. Eftersom prefilling är en engångsuppgift per inferensbegäran kan den avlastas till en centraliserad, kraftfull beräkningsnod som är optimerad för sådana arbetsbelastningar. Det här upplägget möjliggör snabbare bearbetning av stora frågor och minskar belastningen på mindre kapabla enheter, vilket gör det idealiskt för molnbaserade miljöer eller datacentermiljöer där hårdvara med hög kapacitet finns tillgänglig.

Avkodning är däremot minnesbunden och innebär iterativ generering av token, vilket i hög grad är beroende av åtkomst till KV-cacherna. Det kräver mindre beräkningskraft men kräver snabb minnesåtkomst, vilket gör det lämpligt för mindre kraftfull, minnesoptimerad hårdvara som processorer eller edge-enheter. Genom att flytta avkodningen till separat maskinvara - som kan vara närmare slutanvändaren, t.ex. lokala servrar eller edge-enheter - minskar disaggregerad inferens latens och krav på nätverksbandbredd. Denna separation möjliggör flexibel driftsättning, där prefilling körs på avancerade molnservrar och avkodning sker på lokala enheter eller edge-enheter, vilket optimerar resursallokeringen och möjliggör effektiv skalning för applikationer som chattbottar i realtid eller interaktiva AI-system.

Slutsats

Många tekniker för inferensoptimering har nyligen uppfunnits för att förbättra LLM:ernas prestanda.

Att implementera dessa tekniker kräver en djup förståelse för LLM-arkitekturen och den hårdvara du använder, så det är i allmänhet lättare att använda en befintlig inferensmotor som redan har implementerat dessa tekniker som vLLM, TensorRT-LLM, LMDeploy, etc. Vi har faktiskt implementerat dessa tekniker i vår egen inferensmotor på NLP Cloud och vi har skrivit ett blogginlägg om inferensmotorer om du vill distribuera dina egna modeller: du kan läsa det här.

Om du inte kan eller vill distribuera dina egna LLM:er själv kan du använda NLP Cloud och utnyttja snabba generativa AI-modeller i stor skala i produktionen. Prova snabb inferens på NLP Cloud nu!

Om du har frågor om inferensmotorer i allmänhet, tveka inte att fråga oss, det är alltid ett nöje att ge råd!

Julien
CTO på NLP Cloud