Îmbunătățiți performanța modelelor Falcon cu Amazon SageMaker | Amazon Web Services

Îmbunătățiți performanța modelelor Falcon cu Amazon SageMaker | Amazon Web Services

Care este cadrul și configurația optimă pentru găzduirea modelelor de limbă mari (LLM) pentru aplicațiile AI generative generatoare de text? În ciuda abundenței de opțiuni pentru deservirea LLM-urilor, aceasta este o întrebare greu de răspuns din cauza dimensiunii modelelor, diferitelor arhitecturi ale modelelor, cerințelor de performanță ale aplicațiilor și multe altele. The Amazon SageMaker Container de inferență de model mare (LMI). face simplă deservirea LLM-urilor, reunind o serie de cadre și tehnici diferite care optimizează implementarea LLM-urilor. Containerul LMI are o stivă puternică de servire numită Servire DJL care este agnostic pentru LLM-ul de bază. Oferă parametrii de configurare la nivel de sistem care pot fi reglați pentru a extrage cea mai bună performanță a infrastructurii de găzduire pentru un anumit LLM. De asemenea, are suport pentru optimizări recente, cum ar fi dotarea continuă, cunoscută și sub denumirea de loturi iterative sau loturi rulante, care oferă îmbunătățiri semnificative ale debitului.

Într-un timp anterior post, am arătat cum puteți utiliza containerul LMI pentru a implementa familia de modele Falcon pe SageMaker. În această postare, demonstrăm cum să îmbunătățim debitul și latența de servire a lui Falcon-40B cu tehnici precum lotul continuu. De asemenea, oferim o înțelegere intuitivă a parametrilor de configurare furnizați de containerul SageMaker LMI, care vă poate ajuta să găsiți cea mai bună configurație pentru aplicația dvs. din lumea reală.

Fundamentele inferenței generatoare de text pentru LLM

Să ne uităm mai întâi la câteva elemente fundamentale despre cum să efectuăm inferențe pentru LLM pentru generarea de text.

Trecere înainte, activări și memoria cache KV

Având în vedere o secvență de intrare de jetoane, acestea sunt rulate în a forward pass peste toate straturile LLM (cum ar fi Falcon) pentru a genera următorul token. A forward pass se referă la procesul prin care datele de intrare sunt transmise printr-o rețea neuronală pentru a produce o ieșire. În cazul generării de text, trecerea înainte implică introducerea unei semințe sau context inițial în modelul de limbaj și generarea următorului caracter sau simbol din secvență. Pentru a genera o secvență de text, procesul se face adesea în mod iterativ, adică se repetă pentru fiecare pas sau poziție din secvența de ieșire. La fiecare iterație, modelul generează următorul caracter sau simbol, care devine parte a textului generat, iar acest proces continuă până când este generată lungimea dorită a textului.

Generarea de text cu modele de limbaj precum Falcon sau GPT sunt autoregressive. Aceasta înseamnă că modelul generează câte un token la un moment dat, condiționând tokenurile generate anterior. Cu alte cuvinte, la fiecare iterație, modelul ia textul generat anterior ca intrare și prezice următorul simbol pe baza acelui context. După cum se menționează în vLLM: Servire LLM simplă, rapidă și ieftină cu PagedAttention, în acest proces de decodare autoregresivă, toate jetoanele de intrare în LLM își produc tensorii de valoare și cheia de atenție, iar acești tensori sunt păstrați în memoria GPU pentru a genera următoarele jetoane. Acești tensori de cheie și valoare stocați în cache sunt adesea denumiți ca KV cache.

Faze de pre-completare și decodare

Într-un proces de decodare autoregresiv, precum cel utilizat în generarea de text cu modele de limbaj precum Falcon, există de obicei două faze principale: prefill faza și decode fază. Aceste faze sunt cruciale pentru generarea unui text coerent și relevant din punct de vedere contextual.

Faza de pre-umplere include următoarele:

  • Contextul initial – Faza de pre-completare începe cu un context inițial sau un text inițial furnizat de utilizator. Acest context inițial poate fi o propoziție, o frază sau chiar un singur cuvânt. Acesta stabilește punctul de plecare pentru generarea de text și oferă context pentru ceea ce urmează.
  • Condiționarea modelului – Contextul oferit este folosit pentru a condiționa modelul de limbaj. Modelul ia acest context ca intrare și generează următorul simbol (cuvânt sau caracter) din secvență pe baza înțelegerii contextului.
  • Generarea de jetoane – Modelul generează câte un token la un moment dat, prezicând ce ar trebui să urmeze în text. Acest simbol este atașat contextului, extinzându-l efectiv.
  • Proces iterativ – Procesul de generare a jetoanelor se repetă iterativ. La fiecare pas, modelul generează un token luând în considerare contextul actualizat, care include acum tokenurile generate în pașii anteriori.

Faza de pre-umplere continuă până când este îndeplinită o condiție de oprire predeterminată. Această condiție poate fi o lungime maximă pentru textul generat, un simbol specific care semnalează sfârșitul textului sau orice alt criteriu stabilit de utilizator sau de aplicație.

Faza de decodificare include următoarele:

  • Finalizare tranzactiei – După faza de precompletare, aveți un text parțial generat care poate fi incomplet sau întrerupt la un moment dat. Faza de decodare este responsabilă pentru completarea textului pentru a-l face coerent și corect din punct de vedere gramatical.
  • Continuare de la ultimul simbol – În faza de decodare, modelul începe de la ultimul token generat în timpul fazei de pre-umplere. Folosește acest token ca context inițial și generează următorul simbol pentru a continua textul.
  • Finalizare iterativă – La fel ca în faza de precompletare, procesul de generare a jetoanelor este din nou iterativ. Modelul generează câte un jeton la un moment dat, condiționat de jetonele precedente din secvență.
  • Stare de oprire – Faza de decodare are, de asemenea, o condiție de oprire, care ar putea fi aceeași ca în faza de pre-umplere, cum ar fi atingerea unei lungimi maxime sau întâlnirea unui simbol de final de text. Când această condiție este îndeplinită, procesul de generare se oprește.

Combinația dintre fazele de pre-completare și decodare permite modelelor autoregresive să genereze text care se bazează pe un context inițial și produce secvențe de text coerente, relevante din punct de vedere contextual și consistente din punct de vedere contextual.

A se referi la Un sistem de servire distribuit pentru modele generative bazate pe transformatoare pentru o explicație detaliată a procesului.

Optimizarea debitului utilizând loturi dinamice

Până acum, am vorbit doar despre o singură intrare. În practică, ne așteptăm să facem față cererilor multiple care vin aleatoriu de la clienții aplicației pentru a deduce concomitent sau într-un mod eșalonat. În mod tradițional, lotul de bază poate fi utilizat pentru a crește debitul și utilizarea resurselor de calcul ale GPU-ului. Batching-ul este combinarea efectivă a reprezentărilor numerice a mai multor cereri într-un lot și efectuarea de rulări paralele ale trecerilor înainte autoregresive. Această dozare inteligentă se face pe partea de servire. Serverul DJLServing al SageMaker LMI poate fi configurat pentru a grupa mai multe cereri pentru a le procesa în paralel, prin setarea următorilor parametri în deservire.proprietăţi:

  • max_batch_delay = 100 – Întârzierea maximă pentru agregarea loturilor în milisecunde. Valoarea implicită este de 100 de milisecunde.
  • batch_size = 32 – Dimensiunea dinamică a lotului. Valoarea implicită este 1.

Practic, aceasta arată că DJLServing va pune în coadă cererile timp de 100 de milisecunde la un moment dat sau dacă numărul de solicitări care sunt puse în coadă este până la dimensiunea batch_size specificată, lotul va fi programat să ruleze la backend pentru inferență. Aceasta este cunoscută ca dynamic batching. Este dinamic, deoarece dimensiunea lotului se poate schimba în loturi, în funcție de câte solicitări au fost adăugate în perioada respectivă. Cu toate acestea, deoarece cererile pot avea caracteristici diferite (de exemplu, unele solicitări pot avea forma 20 de jetoane de intrare și 500 de jetoane de ieșire, în timp ce altele ar putea fi inversate, cu 500 de jetoane de intrare, dar numai 20 pentru ieșire), unele solicitări ar putea finalizați procesarea mai rapid decât altele din același lot. Acest lucru ar putea duce la subutilizarea GPU-ului în timp ce se așteaptă ca toate solicitările în timpul zborului din lot să finalizeze etapa de decodare, chiar dacă există solicitări suplimentare care așteaptă să fie procesate în coadă. Următoarea diagramă ilustrează acest proces.

Simplu, dinamic loturi vizual

Dynamic Batching Visual – observați ferestrele inactive la sfârșitul cererii 2 și 3

Optimizarea debitului utilizând lotul continuu

cu continuous batching, de asemenea cunoscut ca si iterative or rolling loturi, profităm de diferențele dintre etapele de pre-umplere și decodare. Pentru a activa lotul continuu, DJServing oferă următoarele configurații suplimentare conform serving.properties:

  • motor=MPI – Vă încurajăm să utilizați motorul MPI pentru dozare continuă.
  • opțiune.lot_rulant=auto sau lmi-dist – Vă recomandăm să utilizați auto, deoarece va alege automat cel mai potrivit algoritm de rulare în lot împreună cu alte optimizări în viitor.
  • option.max_rolling_batch_size=32 – Aceasta limitează numărul de solicitări concurente. Valoarea implicită este 32.

Cu lotizarea continuă, stiva de servire (DJLServing) nu așteaptă ca toate solicitările în timpul zborului dintr-un lot să-și finalizeze etapa de decodare. Mai degrabă, la pauze logice (la sfârșitul unei iterații în etapa de decodare), acesta atrage cereri suplimentare care așteaptă în coadă în timp ce lotul curent este încă procesat (de unde și numele lot de rulare). Face această verificare pentru cererile în așteptare la sfârșitul fiecărei iterații a etapei de decodare. Amintiți-vă, pentru fiecare cerere, trebuie să rulăm etapa de precompletare urmată de etapa de decodare secvențială. Deoarece putem procesa toate jetoanele de la solicitarea inițială a unei cereri în paralel pentru etapa de precompletare, de fiecare dată când o nouă solicitare este introdusă, întrerupem temporar etapa de decodificare a solicitărilor în timpul zborului ale lotului - salvăm temporar memoria cache KV. și activări în memorie și rulează etapa de precompletare a noilor solicitări.

Mărimea acestui cache poate fi configurată cu următoarea opțiune:

Când completarea preliminară este completă, combinăm cererile noi și cererile vechi întrerupte într-un nou lot rulant, care poate continua cu etapa de decodare a acestora în paralel. Rețineți că vechile solicitări întrerupte își pot continua etapa de decodare acolo unde au rămas, iar noile solicitări vor începe de la primul lor token nou.

Loturi vizuale continue sau iterative

Continuu sau iterativ loturi vizuale – observați că timpii de inactivitate sunt înlocuiți cu urmărirea cererilor

Poate ți-ai dat seama deja că dotarea continuă este o abordare aproape similară cu care paralelizăm în mod natural sarcinile din viața noastră de zi cu zi. Avem mesaje, e-mailuri, notificări telefonice (cereri potențial noi) care vin la momente aleatorii (analog cu cererile multiple care vin într-un mod eșalonat aleatoriu pentru GPU-uri). Toate acestea se întâmplă în timp ce ne îndeplinim sarcinile în timpul zborului - compunerea e-mailurilor, codificarea, participarea la întâlniri (analog cu sarcinile de procesare curente în GPU-uri). La pauzele logice, ne întrerupem sarcinile în timpul zborului și ne verificăm notificările pentru a decide dacă este necesară o acțiune din partea noastră și, dacă este, o adăugăm la sarcinile noastre în timpul zborului (lotul rulant din viața reală) sau pune-l pe o listă de lucruri de făcut (în coadă).

Punând totul laolaltă: Cum să ne gândim la utilizarea memoriei GPU-urilor

Este recomandat să testați modelul dvs. de încărcare pentru a vedea care configurație este cea mai rentabilă pentru cazul dvs. de utilizare în afaceri. Pentru a construi o înțelegere, să vizualizăm amprenta de memorie a GPU-urilor pe măsură ce modelul este încărcat și pe măsură ce cererile succesive sunt procesate într-un lot continuu. Pentru această postare, să presupunem că încărcăm modelul Falcon-40B pe unul dintre tipurile de instanță G5 care sunt instalate cu GPU-uri NVIDIA A10G, fiecare cu 24 GB de memorie. Rețineți că o înțelegere similară este valabilă pentru tipurile de instanțe p3, p4 și p5, care vin cu seriile GPU V100, A100 și H100.

Următoarea este prezentarea generală a obținerii unei valori aproximative a memoriei totale necesare pentru a servi Falcon-40B:

  • Dimensiunea modelului = Număr de parametri de model (40 miliarde pentru Falcon-40B) x 4 octeți per parametru (pentru FP32) = 160 GB
  • Memoria totală aproximativă necesară pentru a încărca Falcon-40B pentru inferență = Dimensiunea modelului (=160 GB) + KV Cache (Attention Cache) (=*20 GB) + Suplimentare de memorie suplimentară de ML Frameworks (aproximativ 2 GB)
Memorie vizuală

Memory Visual – Înțelegerea amprentei de memorie a unui model Falcon-40B încărcat

Pentru Falcon-40B, dacă comprimăm modelul prin cuantizarea modelului la tipul de date bfloat16 (2 octeți), dimensiunea modelului devine aproximativ 80 GB. După cum puteți vedea, aceasta este încă mai mare decât memoria suportată de un dispozitiv de accelerare, așa că trebuie să adoptăm o tehnică de partiționare a modelului (sharding) cu o tehnică specială. paralelism tensor (TP) abordați și împărțiți modelul pe mai multe dispozitive de accelerație. Să presupunem că am ales g5.24xlarge, care are 4 dispozitive GPU A10G. Dacă configurăm DJLServing (serving.properties) cu următoarele, ne putem aștepta ca cei 80 GB de greutăți ale modelului să fie împărțiți în mod egal în toate cele 4 GPU-uri:

cu tensor_parallel_degree setat la 4, aproximativ 20 GB din memoria GPU de 24 GB (aproximativ 84%) este deja utilizat chiar înainte ca o singură solicitare să fie procesată. Restul de 16% din GPU va fi folosit pentru cache-ul KV pentru cererile primite. Este posibil ca, pentru scenariul dvs. de afaceri și cerințele sale de latență și debit, 2–3 GB din memoria rămasă să fie mai mult decât suficient. Dacă nu, puteți crește dimensiunea instanței la g5.48xlarge, care are 8 GPU și folosește tensor_parallel_degree setat la 8. Într-un astfel de caz, doar aproximativ 10 GB din memoria disponibilă de 24 GB a fiecărui GPU este utilizat pentru greutățile modelului și noi au aproximativ 60% din GPU-ul rămas pentru activări și cache KV. În mod intuitiv, putem vedea că această configurație ne poate permite să avem un debit mai mare. În plus, deoarece avem un tampon mai mare acum, putem crește max_rolling_batch_prefill_tokens și max_rolling_batch_size parametrii pentru a optimiza și mai mult debitul. Împreună, acești doi parametri vor controla prealocările de precompletare de activare și memoria cache KV pentru model. Un număr mai mare pentru acești doi parametri va fi corelat cu un debit mai mare, presupunând că aveți suficient buffer pentru memoria cache KV în memoria GPU.

Dozare continuă cu PagedAttention

PagedAttention este un nou algoritm de optimizare dezvoltat de UC Berkeley care îmbunătățește procesul de lotizare continuă, permițând cache-ului de atenție (cache-ul KV) să fie necontiguu prin alocarea memoriei în pagini sau blocuri de dimensiuni fixe. Acesta este inspirat de conceptele de memorie virtuală și de paginare utilizate de sistemele de operare.

În conformitate cu vLLM hârtie, memoria cache a atenției fiecărei secvențe de jetoane este împărțită în blocuri și mapată la blocuri fizice printr-un tabel de blocuri. În timpul calculării atenției, un nucleu PagedAttention poate folosi tabelul de blocuri pentru a prelua eficient blocurile din memoria fizică. Acest lucru are ca rezultat o reducere semnificativă a risipei de memorie și permite o dimensiune mai mare a lotului, o utilizare crescută a GPU-ului și un debit mai mare.

Comparație de performanță

Pentru a asigura testarea eficientă a sarcinii a configurației dvs. de implementare, este recomandat să începeți prin a lua în considerare scenariul de afaceri și a defini în mod clar caracteristicile de intrare și de ieșire pentru aplicația bazată pe LLM. De exemplu, dacă lucrați la un caz de utilizare de rezumat al centrului de apeluri, intrarea ar putea consta dintr-un text mai mare, cum ar fi o transcriere a chatului cu 500 de simboluri între un agent de serviciu pentru clienți și un client, dar rezultatul poate fi relativ mai mic, în jur de 100. jetoane, reprezentând un rezumat al transcripției. Pe de altă parte, dacă lucrați la un scenariu de generare de cod, intrarea ar putea fi de până la 15 jetoane, cum ar fi „scrieți o implementare eficientă în Python pentru a descrie toate resursele EC2, inclusiv paginarea”, dar rezultatul ar putea fi mult. mai mare, ajungând la 500 de jetoane. De asemenea, este important să luați în considerare dacă obținerea unei latențe mai mici sau maximizarea debitului este prioritatea principală pentru scenariul dvs. specific.

După ce ați obținut o înțelegere cuprinzătoare a scenariului de afaceri, puteți analiza și determina configurația optimă pentru mediul dvs. de găzduire. În acest context, mediul de găzduire cuprinde diverse elemente cheie, inclusiv tipul instanței și alți parametri de configurare, cum ar fi grad_tensor_paralel, max_rolling_batch_size, max_rolling_batch_prefill_tokens, și altele. Obiectivul nostru este să identificăm cea mai eficientă configurație pentru a susține timpul de răspuns, debitul și cerințele de calitate a ieșirii modelului.

În analiza noastră, am evaluat performanța pentru a ilustra beneficiile dotării continue față de dotarea dinamică tradițională. Am folosit configurațiile detaliate în tabelul următor în serving.properties pentru loturi dinamice și loturi iterative, folosind un container LMI pe SageMaker.

Loturi dinamice Dozare continuă Loturi continuă cu PagedAttention

motor=Python

option.model_id=tiiuae/falcon-40b

option.tensor_parallel_degree=8

option.dtype=fp16

batch_size=4

max_batch_delay=100

option.trust_remote_code = adevărat

motor = MPI

option.model_id = {{s3_url}}

option.trust_remote_code = adevărat

opțiune.tensor_paralel_degree = 8

option.max_rolling_batch_size = 32

option.rolling_batch = auto

opțiune.dtype = fp16

option.max_rolling_batch_prefill_tokens = 1024

option.paged_attention = Fals

motor = MPI

option.model_id = {{s3_url}}

option.trust_remote_code = adevărat

opțiune.tensor_paralel_degree = 8

option.max_rolling_batch_size = 32

option.rolling_batch = auto

opțiune.dtype = fp16

option.max_rolling_batch_prefill_tokens = 1024

option.paged_attention = Adevărat

Cele două configurații au fost evaluate pentru Falcon-40B cu tipul de date FP16 implementat pe ml.g5.48xlarge în câteva scenarii diferite care reprezintă aplicații din lumea reală:

  • Un număr mic de jetoane de intrare cu un număr mare de jetoane fiind generate – În acest scenariu, numărul de jetoane de intrare a fost fixat la 32 și au fost generate 128 de jetoane noi
Strategia de loturi Debit (jetoane/sec) Latență p90 (sec.)
Loturi dinamice 5.53 58.34
Dozare continuă 56.04 4.74
Loturi continuă cu PagedAttention 59.18 4.76
  • Se generează o intrare mare cu un număr mic de jetoane – Aici fixăm numărul de jetoane de intrare la 256 și solicităm LLM să rezumă intrarea la 32 de jetoane
Strategia de loturi Debit (jetoane/sec) Latență p90 (sec.)
Loturi dinamice 19.96 59.31
Dozare continuă 46.69 3.88
Loturi continuă cu PagedAttention 44.75 2.67

Putem vedea că lotizarea continuă cu PagedAttention oferă o îmbunătățire a debitului de 10 ori mai mare în scenariul 1 și de 2.3 ori în scenariul 2, comparativ cu utilizarea lotului dinamic pe SageMaker în timp ce se folosește containerul LMI.

Concluzie

În această postare, am analizat modul în care LLM-urile utilizează memoria și am explicat modul în care loturile continue îmbunătățesc debitul folosind un container LMI pe SageMaker. Am demonstrat beneficiile dotării continue pentru Falcon-40B folosind un container LMI pe SageMaker, arătând rezultate de referință. Puteți găsi codul pe GitHub repo.


Despre Autori

Abhigyan ShivadityaAbhi Shivaditya este arhitect senior de soluții la AWS, lucrând cu organizații strategice globale pentru a facilita adoptarea serviciilor AWS în domenii precum inteligența artificială, calculul distribuit, rețelele și stocarea. Expertiza sa constă în Deep Learning în domeniile procesării limbajului natural (NLP) și Computer Vision. Abhi ajută clienții să implementeze eficient modele de învățare automată de înaltă performanță în cadrul ecosistemului AWS.

Improve performance of Falcon models with Amazon SageMaker | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.Dhawal Patel este arhitect principal de învățare automată la AWS. El a lucrat cu organizații, de la întreprinderi mari până la startup-uri mijlocii, pe probleme legate de calculul distribuit și inteligența artificială. El se concentrează pe învățarea profundă, inclusiv pe domeniile NLP și Computer Vision. El îi ajută pe clienți să obțină inferențe de model de înaltă performanță pe SageMaker.

Improve performance of Falcon models with Amazon SageMaker | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.Pinak Panigrahi lucrează cu clienții pentru a construi soluții bazate pe învățarea automată pentru a rezolva probleme strategice de afaceri pe AWS. Când nu este ocupat cu învățarea automată, el poate fi găsit făcând o excursie, citind o carte sau urmărind sporturi.

Improve performance of Falcon models with Amazon SageMaker | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.Abhi Sodhani deține poziția de Senior AI/ML Solutions Architect la AWS, unde este specializat în oferirea de expertiză tehnică și îndrumări cu privire la soluțiile generative AI și ML clienților. Obiectivul său principal este de a ajuta companiile digitale native în realizarea întregului potențial al tehnologiilor generative AI și ML, permițându-le să-și atingă obiectivele de afaceri în mod eficient. Dincolo de eforturile sale profesionale, Abhi manifestă o pasiune puternică pentru activități intelectuale, cum ar fi lectura, precum și implicarea în activități care promovează bunăstarea fizică și mentală, cum ar fi yoga, meditația.

Improve performance of Falcon models with Amazon SageMaker | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.Qing Lan este inginer de dezvoltare software în AWS. El a lucrat la mai multe produse provocatoare în Amazon, inclusiv soluții de inferență ML de înaltă performanță și un sistem de înregistrare de înaltă performanță. Echipa Qing a lansat cu succes primul model cu miliard de parametri în Amazon Advertising, cu o latență foarte scăzută necesară. Qing are cunoștințe aprofundate despre optimizarea infrastructurii și accelerarea Deep Learning.

Timestamp-ul:

Mai mult de la Învățare automată AWS