Optimalizace odvozování je důležitou součástí generativních aplikací umělé inteligence nasazených ve výrobě. Efektivní využití LLM ve velkém měřítku je výzvou a v posledních letech bylo vyvinuto mnoho technik, které mají inferenci zrychlit a zlevnit. Pojďme si tyto techniky v tomto článku projít.
Všechny velké jazykové modely (LLM) jsou založeny na architektuře transformátoru, kterou v roce 2017 vynalezl Vaswani a kol. Architektura transformátoru dosahuje vynikající přesnosti, učení se několika málo snímků a schopností blízkých lidským schopnostem v různých jazykových úlohách. Tyto fundamentální modely, které často obsahují desítky až stovky miliard parametrů, jsou však nákladné na trénování a náročné na zdroje při inferenci. Náklady na inferenci se stupňují s dlouhými vstupními kontexty, které vyžadují značný výpočetní výkon kvůli velkému množství vstupních dat. Efektivní inference se tak stává kritickou výzvou, zejména při správě paměti a výpočetních zdrojů.

Přesněji řečeno, většina známých LLM je pouze dekódovací, jako například GPT-3, GPT-4, LLaMA, Mistral, DeepSeek atd. Tyto modely jsou předem natrénovány na úloze kauzálního modelování a fungují jako prediktory dalšího slova. Zpracovávají posloupnost tokenů jako vstup a autoregresivně vytvářejí následující tokeny, dokud není dosaženo podmínky zastavení.
Odvozování LLM v modelech pouze s dekodérem zahrnuje dvě klíčové fáze: fázi předvyplňování a fázi dekódování. Ve fázi prefill model zpracovává vstupní tokeny, aby vypočítal mezistavy (klíče a hodnoty) pro generování prvního nového tokenu. Tato fáze, připomínající operaci matice-matrice, je vysoce paralelizovaná a efektivně využívá možností GPU. Naopak fáze dekódování generuje tokeny jeden po druhém, přičemž se spoléhá na stavy předchozích tokenů. Tato maticově-vektorová operace je vázána na paměť, protože latenci určuje především přenos dat do GPU, nikoliv rychlost výpočtu, což vede k nedostatečnému využití výpočetního výkonu GPU.
Optimalizace fáze dekódování je ústředním bodem řešení problémů s odvozováním. Řešení zahrnují vývoj účinných mechanismů pozornosti a lepší správu klíčů a hodnot, aby se snížila úzká místa v paměti. Příspěvek upozorňuje na praktické přístupy ke zvýšení výkonu inference za předpokladu, že čtenáři mají základní znalosti o architektuře transformátoru a mechanismech pozornosti. Tyto optimalizace mají zásadní význam pro zlepšení propustnosti a snížení latence v reálných nasazeních LLM.
Další komplikace vyplývá z používání různých tokenizérů v různých LLM, což ovlivňuje srovnatelnost tokenů. Tokeny, které zhruba odpovídají čtyřem anglickým znakům, se liší v reprezentaci v závislosti na tokenizéru, takže přímé porovnání propustnosti inference (např. tokenů za sekundu) je zavádějící. Tato variabilita podtrhuje potřebu standardizovaných hodnotících metrik pro přesné posouzení a porovnání výkonu LLM během inference.
Dávkování je klíčovou strategií pro zlepšení využití a propustnosti GPU ve velkých jazykových modelech (LLM). Zpracováním více požadavků současně pomocí stejného modelu se při dávkování rozdělí paměťové náklady na váhy modelu mezi požadavky, což umožňuje u větších dávek využít větší výpočetní výkon GPU. Velikost dávek však má své meze, protože příliš velké dávky mohou způsobit přetečení paměti kvůli paměťovým nárokům LLM, zejména v souvislosti s ukládáním klíčů do mezipaměti (více o tom později).

Tradiční nebo statické dávkování má svá omezení, protože požadavky v rámci dávky často generují různý počet žetonů dokončení, což vede k různým časům provedení. To způsobuje, že všechny požadavky čekají na dokončení nejpomalejšího z nich, což může být problematické, pokud se délka generování výrazně liší. Pro řešení tohoto problému byly vyvinuty pokročilé techniky, jako je dávkování za letu, které optimalizují výkon.
Dávkování za běhu, známé také jako kontinuální dávkování, řeší problémy, které přináší dynamická povaha pracovních úloh LLM, které mohou sahat od jednoduchých odpovědí chatbotů až po komplexní sumarizaci dokumentů nebo generování kódu. Tyto úlohy vytvářejí výstupy značně rozdílných velikostí, což ztěžuje dávkování a efektivní paralelní provádění požadavků. Na rozdíl od statického dávkování umožňuje dávkování za běhu serveru okamžitě vyřadit dokončené sekvence z dávky a začít zpracovávat nové požadavky, zatímco jiné stále probíhají. Tento přístup výrazně zvyšuje využití GPU tím, že se přizpůsobuje různým časům provádění požadavků v reálných scénářích.
Paralelizace modelů je zásadní strategií pro řízení paměťových a výpočetních nároků rozsáhlých modelů strojového učení jejich rozdělením mezi více grafických procesorů. Tento přístup umožňuje zpracovávat větší modely nebo vstupní dávky, které přesahují paměťovou kapacitu jednoho zařízení, což je nezbytné pro trénování i odvozování v případech, kdy jsou paměťová omezení přísná. Existují různé techniky rozdělování vah modelů, včetně paralelismu potrubí, tenzorového paralelismu a sekvenčního paralelismu, přičemž každá z nich řeší jiné aspekty distribuce modelů. Na rozdíl od datového paralelismu, který se zaměřuje na replikaci modelových vah napříč zařízeními za účelem zpracování větších vstupních dávek během trénování, mají tyto metody větší význam pro snížení paměťové stopy během trénování i inference.

Paralelismus potrubí rozděluje model vertikálně na sekvenční části, přičemž každá část obsahuje podmnožinu vrstev přiřazených samostatnému zařízení. Například při čtyřcestném pipeline nastavení každé zařízení zpracovává čtvrtinu vrstev modelu a předává výstupy dalšímu zařízení v pořadí. To sice výrazně snižuje paměťové nároky na zařízení, ale zároveň to přináší neefektivitu známou jako "bubliny v potrubí", kdy zařízení mohou být nečinná, zatímco čekají na výstupy z předchozích vrstev. Mikrodávkování, které rozděluje vstupní dávky do menších dílčích dávek pro sekvenční zpracování, může tyto bubliny omezit, ale ne zcela odstranit, protože doba nečinnosti přetrvává během předávání a zpětného předávání.
Naproti tomu tenzorový paralelismus rozděluje jednotlivé vrstvy horizontálně do menších výpočetních bloků, které lze provádět nezávisle na různých zařízeních. To je zvláště efektivní u transformačních komponent, jako jsou bloky pozornosti a vícevrstvé perceptrony (MLP), kde lze například různé hlavy pozornosti přiřadit samostatným zařízením pro paralelní výpočet. Tensorový paralelismus je však méně efektivní pro operace, jako je LayerNorm a Dropout, které nelze snadno rozdělit a musí být replikovány napříč zařízeními, což vede k nadbytečnému využívání paměti pro ukládání aktivací. Toto omezení zdůrazňuje potřebu doplňkových přístupů k optimalizaci efektivity paměti.
Sekvenční paralelismus řeší paměťovou neefektivitu operací, jako je LayerNorm a Dropout, jejich rozdělením podél dimenze vstupní sekvence, čímž se využívá jejich nezávislost na jednotlivých prvcích sekvence. Tato metoda snižuje paměťovou náročnost redundantních aktivací, což z ní činí cenný doplněk tenzorového paralelismu. Tyto paralelizační techniky se vzájemně nevylučují a lze je kombinovat za účelem další optimalizace velkých jazykových modelů (LLM). Specifické optimalizační strategie pro modul pozornosti mohou navíc zvýšit škálovatelnost a snížit paměťové nároky na procesor, což umožní efektivnější trénování a odvozování velkých modelů.
V roce 2017 Vaswani a kol. představili v článku *Attention Is All You Need* model Transformer, jehož základem je sebepozorování. Sebepozornost umožňuje modelu vyhodnocovat relevanci různých slov ve větě vůči sobě navzájem, což zlepšuje kontextové porozumění pro úlohy, jako je zpracování přirozeného jazyka. Článek formalizoval self-attention, zejména prostřednictvím mechanismu SDPA (scaled dot-product attention), který mapuje dvojice dotaz a klíč-hodnota na výstup, což z něj činí klíčovou součást moderních neuronových sítí. Zde jsou uvedeny některé z nejdůležitějších technik pro optimalizaci výpočtů pozornosti:

Pozornost s více hlavami (Multi-head attention, MHA) navazuje na SDPA tím, že paralelně provádí více operací pozornosti, z nichž každá má odlišné projekce matic dotazů, klíčů a hodnot. Tyto paralelní operace neboli "hlavy" se zaměřují na různé reprezentační podprostory, čímž obohacují modelové chápání vstupních dat. Výstupy z těchto hlav jsou spojeny a lineárně promítnuty, čímž je zachována výpočetní efektivita srovnatelná s pozorností jedné hlavy tím, že se sníží dimenzionalita každé hlavy (např. vydělením dimenze modelu počtem hlav, např. 8).
Pozornost na více dotazů (MQA) optimalizuje MHA pro inferenci sdílením projekcí klíčů a hodnot mezi více hlavami pozornosti při zachování projekcí více dotazů. Tím se snižují nároky na šířku paměťového pásma a velikost mezipaměti klíč-hodnota (KV), což umožňuje větší velikosti dávek a lepší využití výpočetního výkonu. MQA však může mírně snížit přesnost a modely, které ji využívají, vyžadují trénink nebo jemné doladění se zapnutou MQA, aby si zachovaly výkonnost.
Skupinová pozornost dotazů (GQA) vyvažuje MHA a MQA seskupováním hlav dotazů a sdílením projekcí klíčových hodnot v rámci každé skupiny, čímž dosahuje kvality blízké MHA s výpočetní efektivitou blížící se MQA. Modely jako Llama 2 70B používají GQA a modely vyškolené pomocí MHA lze přizpůsobit GQA s minimálním dodatečným školením. Jak MQA, tak GQA snižují nároky na paměť cache KV, ačkoli jsou nadále nutné další optimalizace správy cache.
FlashAttention vylepšuje mechanismy pozornosti změnou pořadí výpočtů tak, aby efektivněji využívaly hierarchie paměti GPU. Na rozdíl od tradičního zpracování po vrstvách FlashAttention slučuje operace a používá "tiling" k výpočtu malých částí výstupní matice najednou, čímž minimalizuje operace čtení/zápisu do paměti. Tento algoritmus přesné pozornosti s ohledem na vstupy a výstupy se bez problémů integruje do stávajících modelů bez nutnosti úprav a nabízí výrazné zrychlení díky optimalizaci pohybu dat.
KV caching je kritická optimalizační technika používaná ve fázi dekódování velkých jazykových modelů (LLM) ke zlepšení efektivity výpočtů vlastní pozornosti. V této fázi závisí každý generovaný token na tenzorech klíče (K) a hodnoty (V) všech předchozích tokenů, včetně těch, které byly vypočteny během fáze předvyplňování a následných kroků dekódování. Namísto opětovného výpočtu těchto tenzorů pro každý token v každém časovém kroku je KV caching ukládá do paměti GPU a nové tenzory přidává do cache v okamžiku jejich výpočtu. Obvykle se pro každou vrstvu modelu udržuje samostatná mezipaměť KV, což výrazně snižuje počet nadbytečných výpočtů a urychluje proces dekódování.

Paměťové nároky pro LLM na GPU jsou dány především dvěma složkami: váhami modelů a cache KV. Modelové váhy, které se skládají z parametrů modelu, zabírají značnou část paměti; například model se 7 miliardami parametrů, jako je Llama 2 7B v 16bitové přesnosti, vyžaduje přibližně 14 GB. Naproti tomu vyrovnávací paměť KV uchovává tenzory vlastní pozornosti, aby se zabránilo opětovnému výpočtu, přičemž její velikost je určena faktory, jako je počet vrstev, hlav pozornosti, rozměry hlav a přesnost. Pro každý token se velikost cache vypočítá jako 2 * počet_vrstev * (počet_hlav * dim_hlav) * přesnost_v_bytech, kde faktor 2 zohledňuje matice K i V. Pro dávku vstupů se celková velikost vyrovnávací paměti KV škáluje s velikostí dávky a délkou sekvence a může dosáhnout značných velikostí, například ~2 GB pro model Llama 2 7B s délkou sekvence 4 096 a velikostí dávky 1.
Efektivní správa mezipaměti KV představuje výzvu kvůli jejímu lineárnímu růstu s velikostí dávky a délkou sekvence, což může omezovat propustnost a komplikovat zpracování vstupů s dlouhým kontextem. Běžná neefektivita vzniká v důsledku statického nadměrného poskytování, kdy je paměť rezervována pro maximální podporovanou délku sekvence (např. 2 048 tokenů) bez ohledu na skutečnou velikost vstupu. To vede ke značnému plýtvání pamětí nebo její fragmentaci, protože velká část rezervovaného prostoru často zůstává po celou dobu trvání požadavku nevyužita, čímž jsou vázány cenné paměťové zdroje GPU.
Algoritmus PagedAttention řeší tyto neefektivity novým přístupem inspirovaným stránkováním operačního systému. Rozděluje vyrovnávací paměť KV na bloky pevné velikosti, z nichž každý představuje určitý počet tokenů, které mohou být v paměti uloženy nesouvisle. Tyto bloky sleduje tabulka bloků, která je podle potřeby načítá během výpočtů pozornosti. Při generování nových tokenů se dynamicky alokují další bloky. Tato metoda minimalizuje plýtvání pamětí tím, že eliminuje potřebu souvislé alokace a nadměrného přidělování, umožňuje větší velikosti dávek a zvyšuje propustnost, čímž představuje významný pokrok ve správě paměti cache KV pro LLM.
V této části se zabýváme různými technikami optimalizace velkých jazykových modelů (LLM) s cílem snížit jejich spotřebu paměti a zvýšit výkon na grafických procesorech. Mezi klíčové metody patří kvantizace, řídkost a destilace, přičemž každá z nich se zaměřuje na jiné aspekty efektivity modelu. Tyto techniky upravují váhy modelů, využívají hardwarovou akceleraci GPU a přenášejí znalosti do menších modelů, což umožňuje provozovat větší modely na omezeném hardwaru při zachování výkonu. Tyto metody mohou snížit přesnost modelu, proto by měly být používány s opatrností.
Kvantizace snižuje přesnost vah a aktivací modelu, obvykle z 32 nebo 16 bitů na 8 nebo méně bitů, což modelům umožňuje zabírat méně paměti a efektivněji přenášet data. Zatímco kvantizace vah je jednoduchá vzhledem k jejich fixní povaze po tréninku, kvantizace aktivací je složitější kvůli odlehlým hodnotám, které rozšiřují jejich dynamický rozsah. Techniky jako LLM.int8() to řeší selektivním použitím vyšší přesnosti na určité aktivace nebo opětovným použitím dynamického rozsahu kvantizovaných vah pro aktivace, ačkoli GPU mohou pro operace vyžadovat převod vah zpět na vyšší přesnost.
Řídkost zahrnuje ořezávání hodnot modelu blízkých nule, čímž se vytvářejí řídké matice, které vyžadují méně paměti. Grafické procesory podporují strukturovanou řídkost, například reprezentují dvě z každých čtyř hodnot jako nuly, což urychluje výpočty. Kombinace řídkosti s kvantizací může dále zvýšit rychlost provádění. Výzkum pokračuje ve zkoumání optimálních řídkých reprezentací pro LLM, což naznačuje slibnou cestu ke zvýšení rychlosti inference.
Destilace přenáší znalosti z většího modelu "učitele" do menšího modelu "studenta", čímž se zmenšuje velikost a zároveň se zachovává výkon. DistilBERT například dosahuje 40% snížení velikosti a 60% zvýšení rychlosti ve srovnání s BERT, přičemž si zachovává 97 % svých schopností. Distilace může zahrnovat napodobování výstupů učitele nebo použití dat vytvořených učitelem pro trénink, přičemž metody jako "Distill Step by Step!" zahrnují zdůvodnění efektivního učení. Omezující licence na mnoho pokročilých LLM však omezují dostupnost vhodných modelů učitelů pro destilaci.
Spekulativní odvozování, známé také jako spekulativní vzorkování nebo asistované generování, je metoda paralelizace provádění autoregresivních velkých jazykových modelů (LLM), jako jsou modely typu GPT, které obvykle generují text token po tokenu. Při standardním provádění je každý token kontextově závislý na všech předchozích tokenech, což znemožňuje paralelní generování, protože n-tý token musí být vygenerován před (n+1)-tým. Spekulativní odvozování řeší tento problém tím, že používá "levnější" návrhový model k předpovědi více budoucích tokenů současně, které jsou pak paralelně ověřovány nebo zamítány hlavním modelem, což umožňuje rychlejší generování textu.
Tento proces zahrnuje generování návrhu pokračování několika tokenů pomocí metody méně náročné na zdroje, po níž následuje paralelní ověření hlavním modelem s použitím návrhu jako spekulativního kontextu. Pokud se ověřovací model shoduje s návrhem tokenů, jsou tyto tokeny přijaty; v opačném případě jsou neshodující se tokeny a následné tokeny vyřazeny a proces se opakuje s novým návrhem. Návrhy tokenů lze generovat pomocí různých přístupů, například trénováním více modelů, jemným doladěním více hlav na předem natrénovaném modelu k předpovědi budoucích tokenů nebo použitím menšího návrhu modelu vedle většího, schopnějšího ověřovacího modelu, přičemž každý z nich má své vlastní kompromisy.
Disagregované odvozování je technika, při níž jsou výpočetní úlohy rozděleny mezi různý hardware, aby se optimalizoval výkon, náklady a využití zdrojů. Konkrétně odděluje fáze předvyplňování a dekódování. Rozdělením těchto fází lze každou z nich přiřadit hardwaru, který nejlépe vyhovuje jejím výpočetním nárokům, čímž se zlepší efektivita a škálovatelnost.

Předvyplňování je výpočetně náročné, protože vyžaduje značné násobení matic pro zpracování celé vstupní výzvy a vytvoření KV cache. V této fázi je výhodné využít vysoce výkonný hardware, jako jsou grafické procesory nebo jednotky TPU, které vynikají v paralelních výpočtech. Protože předvyplnění je jednorázová úloha na jednu inferenční žádost, lze ji přenést na centralizovaný, výkonný výpočetní uzel optimalizovaný pro takovou zátěž. Toto nastavení umožňuje rychlejší zpracování velkých výzev a snižuje zátěž méně výkonných zařízení, takže je ideální pro prostředí cloudů nebo datových center, kde je k dispozici vysoce výkonný hardware.
Naproti tomu dekódování je vázáno na paměť a zahrnuje iterativní generování tokenů, přičemž se do značné míry spoléhá na přístup ke KV cache. Vyžaduje menší výpočetní výkon, ale potřebuje rychlý přístup do paměti, takže je vhodné pro méně výkonný, paměťově optimalizovaný hardware, jako jsou procesory nebo okrajová zařízení. Přesunutím dekódování na samostatný hardware - potenciálně blíže ke koncovému uživateli, jako jsou lokální servery nebo okrajová zařízení - snižuje disagregovaná inference latenci a nároky na šířku pásma sítě. Toto oddělení umožňuje flexibilní nasazení, kdy předvyplňování běží na špičkových cloudových serverech a dekódování probíhá na lokálních nebo okrajových zařízeních, což optimalizuje přidělování zdrojů a umožňuje efektivní škálování pro aplikace, jako jsou chatboti v reálném čase nebo interaktivní systémy umělé inteligence.
V poslední době bylo vynalezeno mnoho technik optimalizace inference s cílem zlepšit výkonnost LLM.
Implementace těchto technik vyžaduje hlubokou znalost architektury LLM a používaného hardwaru, takže je obecně jednodušší použít existující inferenční engine, který již tyto techniky implementoval, jako je vLLM, TensorRT-LLM, LMDeploy atd. Ve skutečnosti jsme tyto techniky implementovali v našem vlastním inferenčním enginu v NLP Cloud a pokud chcete nasadit vlastní modely, napsali jsme příspěvek na blogu o inferenčních enginech: si můžete přečíst zde.
Pokud nemůžete nebo nechcete sami nasadit vlastní LLM, můžete využít službu NLP Cloud a využívat rychlé generativní modely AI ve velkém měřítku v produkci. Vyzkoušejte si rychlé odvozování v NLP Cloud!
Pokud máte dotazy týkající se inferenčních motorů obecně, neváhejte se nás zeptat, vždy vám rádi poradíme!
Julien
Technický ředitel společnosti NLP Cloud