Maximizați performanța Stable Diffusion și reduceți costurile de inferență cu AWS Inferentia2 | Amazon Web Services

Maximizați performanța Stable Diffusion și reduceți costurile de inferență cu AWS Inferentia2 | Amazon Web Services

Modelele AI generative au cunoscut o creștere rapidă în ultimele luni datorită capacităților sale impresionante de a crea text, imagini, cod și audio realiste. Printre aceste modele, modelele Stable Diffusion se remarcă prin puterea lor unică în crearea de imagini de înaltă calitate pe baza solicitărilor de text. Stable Diffusion poate genera o mare varietate de imagini de înaltă calitate, inclusiv portrete realiste, peisaje și chiar artă abstractă. Și, ca și alte modele AI generative, modelele Stable Diffusion necesită calcul puternic pentru a oferi o inferență cu latență scăzută.

În această postare, vă arătăm cum puteți rula modele Stable Diffusion și puteți obține performanțe ridicate la cel mai mic cost Cloud Elastic de calcul Amazon (Amazon EC2) folosind Instanțe Amazon EC2 Inf2 powered by AWS Inferentia2. Ne uităm la arhitectura unui model de difuzie stabilă și parcurgem pașii compilării unui model de difuzie stabilă folosind AWS Neuron și implementarea acestuia într-o instanță Inf2. De asemenea, discutăm despre optimizările pe care Neuron SDK le face automat pentru a îmbunătăți performanța. Puteți rula ambele versiuni Stable Diffusion 2.1 și 1.5 pe AWS Inferentia2 în mod rentabil. În cele din urmă, arătăm cum puteți implementa un model Stable Diffusion într-o instanță Inf2 cu Amazon SageMaker.

Dimensiunea modelului Stable Diffusion 2.1 în virgulă mobilă 32 (FP32) este de 5 GB și 2.5 GB în bfoat16 (BF16). O singură instanță inf2.xlarge are un accelerator AWS Inferentia2 cu 32 GB de memorie HBM. Modelul Stable Diffusion 2.1 se poate încadra pe o singură instanță inf2.xlarge. Stable Diffusion este un model text-to-image pe care îl puteți utiliza pentru a crea imagini de diferite stiluri și conținut, pur și simplu oferind un mesaj text ca intrare. Pentru a afla mai multe despre arhitectura modelului Stable Diffusion, consultați Creați imagini de înaltă calitate cu modele Stable Diffusion și implementați-le în mod eficient din punct de vedere al costurilor cu Amazon SageMaker.

Modul în care Neuron SDK optimizează performanța Stable Diffusion

Înainte de a putea implementa modelul Stable Diffusion 2.1 pe instanțe AWS Inferentia2, trebuie să compilam componentele modelului folosind Neuron SDK. Neuron SDK, care include un compilator de deep learning, runtime și instrumente, compilează și optimizează automat modelele de deep learning, astfel încât acestea să poată rula eficient pe instanțe Inf2 și să extragă performanța completă a acceleratorului AWS Inferentia2. Avem exemple disponibile pentru modelul Stable Diffusion 2.1 pe GitHub repo. Acest notebook prezintă un exemplu de la capăt la capăt al modului de compilare a unui model de difuzie stabilă, salvare a modelelor Neuron compilate și încărcare a acestuia în timpul de execuție pentru inferență.

Noi folosim StableDiffusionPipeline din Fața Îmbrățișată diffusers bibliotecă pentru a încărca și a compila modelul. Compilăm apoi toate componentele modelului pentru utilizarea Neuron torch_neuronx.trace() și salvați modelul optimizat ca TorchScript. Procesele de compilare pot consuma destul de mult memoria, necesitând o cantitate semnificativă de memorie RAM. Pentru a evita acest lucru, înainte de a urmări fiecare model, creăm un deepcopy a porțiunii de conductă care este urmărită. După aceasta, ștergem obiectul pipeline din memorie folosind del pipe. Această tehnică este deosebit de utilă atunci când se compilează pe instanțe cu RAM scăzută.

În plus, efectuăm și optimizări ale modelelor Stable Diffusion. UNet deține aspectul cel mai intensiv din punct de vedere computațional al inferenței. Componenta UNet operează pe tensori de intrare care au o dimensiune a lotului de doi, generând un tensor de ieșire corespunzător și cu o dimensiune a lotului de doi, pentru a produce o singură imagine. Elementele din aceste loturi sunt complet independente unele de altele. Putem profita de acest comportament pentru a obține o latență optimă prin rularea unui lot pe fiecare nucleu Neuron. Compilăm UNet pentru un lot (prin folosirea tensoarelor de intrare cu un lot), apoi folosim torch_neuronx.DataParallel API pentru a încărca acest model de lot unic pe fiecare nucleu. Ieșirea acestui API este un modul fără întreruperi cu două loturi: putem transmite către UNet intrările a două loturi și este returnată o ieșire cu două loturi, dar în interior, cele două modele cu un singur lot rulează pe cele două nuclee Neuron. . Această strategie optimizează utilizarea resurselor și reduce latența.

Compilați și implementați un model Stable Diffusion pe o instanță Inf2 EC2

Pentru a compila și implementa modelul Stable Diffusion pe o instanță Inf2 EC2, conectați-vă la Consola de administrare AWS și creați o instanță inf2.8xlarge. Rețineți că o instanță inf2.8xlarge este necesară numai pentru compilarea modelului deoarece compilarea necesită o memorie gazdă mai mare. Modelul Stable Diffusion poate fi găzduit pe o instanță inf2.xlarge. Puteți găsi cel mai recent AMI cu biblioteci Neuron folosind următoarele Interfața liniei de comandă AWS Comanda (AWS CLI):

aws ec2 describe-images --region us-east-1 --owners amazon --filters 'Name=name,Values=Deep Learning AMI Neuron PyTorch 1.13.? (Amazon Linux 2) ????????' 'Name=state,Values=available' --query 'reverse(sort_by(Images, &CreationDate))[:1].ImageId' --output text

Pentru acest exemplu, am creat o instanță EC2 utilizând Deep Learning AMI Neuron PyTorch 1.13 (Ubuntu 20.04). Puteți crea apoi un mediu de laborator JupyterLab conectându-vă la instanță și rulând următorii pași:

run source /opt/aws_neuron_venv_pytorch/bin/activate
pip install jupyterlab
jupyter-lab

Pe un notebook se află toți pașii pentru compilarea și găzduirea modelului GitHub.

Să ne uităm la pașii de compilare pentru unul dintre blocurile codificatoare de text. Alte blocuri care fac parte din conducta Stable Diffusion pot fi compilate în mod similar.

Primul pas este să încărcați modelul pre-antrenat de la Hugging Face. The StableDiffusionPipeline.from_pretrained metoda încarcă modelul pre-antrenat în obiectul nostru pipeline, pipe. Creăm apoi un deepcopy a codificatorului de text din conducta noastră, clonându-l efectiv. The del pipe Comanda este apoi folosită pentru a șterge obiectul pipeline original, eliberând memoria care a fost consumată de acesta. Aici, cuantificăm modelul la greutăți BF16:

model_id = "stabilityai/stable-diffusion-2-1-base"
pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.bfloat16)
text_encoder = copy.deepcopy(pipe.text_encoder)
del pipe

Acest pas implică împachetarea codificatorului nostru de text cu NeuronTextEncoder învelitoare. Ieșirea unui modul de codificare de text compilat va fi de dict. Îl convertim în a list tastați folosind acest înveliș:

text_encoder = NeuronTextEncoder(text_encoder)

Inițializam tensorul PyTorch emb cu unele valori. The emb tensorul este folosit ca exemplu de intrare pentru torch_neuronx.trace funcţie. Această funcție urmărește codificatorul nostru de text și îl compilează într-un format optimizat pentru Neuron. Calea directorului pentru modelul compilat este construită prin unire COMPILER_WORKDIR_ROOT cu subdirectorul text_encoder:

emb = torch.tensor([...])
text_encoder_neuron = torch_neuronx.trace(
        text_encoder.neuron_text_encoder,
        emb,
        compiler_workdir=os.path.join(COMPILER_WORKDIR_ROOT, 'text_encoder'),
        )

Codificatorul de text compilat este salvat folosind torch.jit.save. Este stocat sub numele de fișier model.pt în fișierul text_encoder directorul spațiului de lucru al compilatorului nostru:

text_encoder_filename = os.path.join(COMPILER_WORKDIR_ROOT, 'text_encoder/model.pt')
torch.jit.save(text_encoder_neuron, text_encoder_filename)

caiet include pași similari pentru compilarea altor componente ale modelului: UNet, decodor VAE și VAE post_quant_conv. După ce ați compilat toate modelele, puteți încărca și rula modelul urmând acești pași:

  1. Definiți căile pentru modelele compilate.
  2. Încărcați un pre-antrenat StableDiffusionPipeline model, cu configurația specificată pentru a utiliza tipul de date bfloat16.
  3. Încărcați modelul UNet pe două nuclee Neuron folosind torch_neuronx.DataParallel API. Acest lucru permite efectuarea de inferențe paralele a datelor, ceea ce poate accelera semnificativ performanța modelului.
  4. Încărcați părțile rămase ale modelului (text_encoder, decoder, și post_quant_conv) pe un singur nucleu neuron.

Puteți rula apoi canalul furnizând text de intrare ca solicitări. Următoarele sunt câteva imagini generate de model pentru solicitări:

  • Portretul lui Renaud Sechan, stilou și cerneală, desene complicate, de Craig Mullins, Ruan Jia, Kentaro Miura, Greg Rutkowski, Loundraw

Maximize Stable Diffusion performance and lower inference costs with AWS Inferentia2 | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.

  • Portret al bătrânului miner de cărbune în secolul al XIX-lea, pictură frumoasă, cu pictură pe față foarte detaliată de Greg Rutkowski

Maximize Stable Diffusion performance and lower inference costs with AWS Inferentia2 | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.

  • Un castel în mijlocul unei păduri

Maximize Stable Diffusion performance and lower inference costs with AWS Inferentia2 | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.

Găzduiește Stable Diffusion 2.1 pe AWS Inferentia2 și SageMaker

Găzduirea modelelor Stable Diffusion cu SageMaker necesită, de asemenea, compilarea cu Neuron SDK. Puteți finaliza compilarea din timp sau în timpul rulării utilizând containerele Large Model Inference (LMI). Compilarea din timp permite timpi mai rapidi de încărcare a modelului și este opțiunea preferată.

Containerele SageMaker LMI oferă două moduri de implementare a modelului:

  • O opțiune fără cod în care oferim doar un serving.properties fișier cu configurațiile necesare
  • Aduceți propriul script de inferență

Ne uităm la ambele soluții și trecem peste configurații și scriptul de inferență (model.py). În această postare, demonstrăm implementarea utilizând un model pre-compilat stocat într-un Serviciul Amazon de stocare simplă (Amazon S3) găleată. Puteți utiliza acest model precompilat pentru implementările dvs.

Configurați modelul cu un script furnizat

În această secțiune, arătăm cum să configurați containerul LMI pentru a găzdui modelele Stable Diffusion. Notebook-ul SD2.1 disponibil pe GitHub. Primul pas este crearea pachetului de configurare a modelului conform următoarei structuri de directoare. Scopul nostru este să folosim configurațiile minime ale modelului necesare pentru a găzdui modelul. Structura de directoare necesară este următoarea:

<config-root-directory> / 
    ├── serving.properties
    │   
    └── model.py [OPTIONAL]

Apoi, creăm deservire.proprietăţi fișier cu următorii parametri:

%%writefile code_sd/serving.properties
engine=Python
option.entryPoint=djl_python.transformers-neuronx
option.use_stable_diffusion=True
option.model_id=s3url
option.tensor_parallel_degree=2
option.dtype=bf16

Parametrii specifică următoarele:

  • opțiune.model_id – Containerele LMI folosesc s5cmd pentru a încărca modelul din locația S3 și, prin urmare, trebuie să specificăm locația unde sunt greutățile noastre compilate.
  • opțiune.entryPoint – Pentru a utiliza handlerele încorporate, specificăm clasa transformers-neuronx. Dacă aveți un script de inferență personalizat, trebuie să îl furnizați în schimb.
  • opțiune.dtype – Aceasta specifică încărcarea greutăților într-o anumită dimensiune. Pentru această postare, folosim BF16, care reduce și mai mult cerințele noastre de memorie față de FP32 și scade latența din acest motiv.
  • opțiune.tensor_paralel_degree – Acest parametru specifică numărul de acceleratoare pe care le folosim pentru acest model. Acceleratorul de cip AWS Inferentia2 are două nuclee Neuron și, prin urmare, specificarea unei valori de 2 înseamnă că folosim un accelerator (două nuclee). Aceasta înseamnă că acum putem crea mai mulți lucrători pentru a crește debitul punctului final.
  • opțiune.motor – Acesta este setat la Python pentru a indica că nu vom folosi alte compilatoare precum DeepSpeed ​​sau Faster Transformer pentru această găzduire.

Adu-ți propriul scenariu

Dacă doriți să aduceți propriul script de inferență personalizat, trebuie să eliminați option.entryPoint din serving.properties. Containerul LMI în acest caz va căuta a model.py fișier în aceeași locație ca și serving.properties și folosiți-o pentru a rula inferențele.

Creați-vă propriul script de inferență (model.py)

Crearea propriului script de inferență este relativ simplă folosind containerul LMI. Containerul necesită dvs model.py fișier pentru a avea o implementare a următoarei metode:

def handle(inputs: Input) which returns an object of type Outputs

Să examinăm câteva dintre zonele critice ale caiet atasat, care demonstrează funcția aduceți propriul script.

Inlocuieste cross_attention modul cu versiunea optimizată:

# Replace original cross-attention module with custom cross-attention module for better performance
    CrossAttention.get_attention_scores = get_attention_scores
Load the compiled weights for the following
text_encoder_filename = os.path.join(COMPILER_WORKDIR_ROOT, 'text_encoder.pt')
decoder_filename = os.path.join(COMPILER_WORKDIR_ROOT, 'vae_decoder.pt')
unet_filename = os.path.join(COMPILER_WORKDIR_ROOT, 'unet.pt')
post_quant_conv_filename =. os.path.join(COMPILER_WORKDIR_ROOT, 'vae_post_quant_conv.pt')

Acestea sunt numele fișierului de greutăți compilat pe care l-am folosit la crearea compilațiilor. Simțiți-vă liber să schimbați numele fișierelor, dar asigurați-vă că numele fișierelor cu greutăți corespund cu ceea ce specificați aici.

Apoi trebuie să le încărcăm folosind Neuron SDK și să le setăm în greutățile reale ale modelului. Când încărcați greutățile optimizate UNet, rețineți că specificăm, de asemenea, numărul de nuclee Neuron pe care trebuie să le încărcăm. Aici, încărcăm într-un singur accelerator cu două nuclee:

# Load the compiled UNet onto two neuron cores.
    pipe.unet = NeuronUNet(UNetWrap(pipe.unet))
    logging.info(f"Loading model: unet:created")
    device_ids = [idx for idx in range(tensor_parallel_degree)]
   
    pipe.unet.unetwrap = torch_neuronx.DataParallel(torch.jit.load(unet_filename), device_ids, set_dynamic_batching=False)
   
 
    # Load other compiled models onto a single neuron core.
 
    # - load encoders
    pipe.text_encoder = NeuronTextEncoder(pipe.text_encoder)
    clip_compiled = torch.jit.load(text_encoder_filename)
    pipe.text_encoder.neuron_text_encoder = clip_compiled
    #- load decoders
    pipe.vae.decoder = torch.jit.load(decoder_filename)
    pipe.vae.post_quant_conv = torch.jit.load(post_quant_conv_filename)

Rularea inferenței cu un prompt invocă obiectul pipe pentru a genera o imagine.

Creați punctul final SageMaker

Folosim API-urile Boto3 pentru a crea un punct final SageMaker. Parcurgeți următorii pași:

  1. Creați tarball-ul doar cu porția și opționalul model.py fișiere și încărcați-l pe Amazon S3.
  2. Creați modelul folosind containerul de imagini și tarball-ul modelului încărcat mai devreme.
  3. Creați configurația punctului final utilizând următorii parametri cheie:
    1. Utilizați un ml.inf2.xlarge instanță.
    2. set ContainerStartupHealthCheckTimeoutInSeconds la 240 pentru a se asigura că verificarea de sănătate începe după ce modelul este implementat.
    3. set VolumeInGB la o valoare mai mare, astfel încât să poată fi utilizat pentru încărcarea greutăților modelului care au o dimensiune de 32 GB.

Creați un model SageMaker

După ce creați fișierul model.tar.gz și îl încărcați pe Amazon S3, trebuie să creăm un model SageMaker. Folosim containerul LMI și artefactul model de la pasul anterior pentru a crea modelul SageMaker. SageMaker ne permite să personalizăm și să injectăm diverse variabile de mediu. Pentru acest flux de lucru, putem lăsa totul ca implicit. Vezi următorul cod:

inference_image_uri = (
    f"763104351884.dkr.ecr.{region}.amazonaws.com/djl-inference:0 djl-serving-inf2"
)

Creați obiectul model, care creează, în esență, un container de blocare care este încărcat în instanță și utilizat pentru deducere:

model_name = name_from_base(f"inf2-sd")
create_model_response = boto3_sm_client.create_model(
    ModelName=model_name,
    ExecutionRoleArn=role,
    PrimaryContainer={"Image": inference_image_uri, "ModelDataUrl": s3_code_artifact},
)

Creați un punct final SageMaker

În această demonstrație, folosim o instanță ml.inf2.xlarge. Trebuie să setăm VolumeSizeInGB parametrii pentru a oferi spațiul de disc necesar pentru încărcarea modelului și a greutăților. Acest parametru este aplicabil instanțelor care acceptă Magazin Amazon Elastic Block (Amazon EBS) atașament de volum. Putem lăsa expirarea timpului de descărcare a modelului și verificarea stării de pornire a containerului la o valoare mai mare, ceea ce va oferi timp suficient pentru ca containerul să tragă greutățile din Amazon S3 și să se încarce în acceleratoarele AWS Inferentia2. Pentru mai multe detalii, consultați CreateEndpointConfig.

endpoint_config_response = boto3_sm_client.create_endpoint_config( EndpointConfigName=endpoint_config_name,
    ProductionVariants=[
        {
            "VariantName": "variant1",
            "ModelName": model_name,
            "InstanceType": "ml.inf2.xlarge", # - 
            "InitialInstanceCount": 1,
            "ContainerStartupHealthCheckTimeoutInSeconds": 360, 
            "VolumeSizeInGB": 400
        },
    ],
)

În cele din urmă, creăm un punct final SageMaker:

create_endpoint_response = boto3_sm_client.create_endpoint(
    EndpointName=f"{endpoint_name}", EndpointConfigName=endpoint_config_name
)

Invocați punctul final al modelului

Acesta este un model generativ, așa că transmitem promptul pe care modelul îl folosește pentru a genera imaginea. Sarcina utilă este de tipul JSON:

response_model = boto3_sm_run_client.invoke_endpoint( EndpointName=endpoint_name,
    Body=json.dumps(
        {
            "prompt": "Mountain Landscape", 
            "parameters": {} # 
        }
    ), 
    ContentType="application/json",
)

Evaluarea comparativă a modelului de difuzie stabilă pe Inf2

Am efectuat câteva teste pentru a compara modelul Stable Diffusion cu tipul de date BF 16 pe Inf2 și suntem capabili să obținem numere de latență care rivalizează sau depășesc unele dintre celelalte acceleratoare pentru Stable Diffusion. Acest lucru, împreună cu costul mai scăzut al cipurilor AWS Inferentia2, face din aceasta o propunere extrem de valoroasă.

Următoarele numere provin din modelul Stable Diffusion implementat pe o instanță inf2.xl. Pentru mai multe informații despre costuri, consultați Instanțele Amazon EC2 Inf2.

Model Rezoluţie Tip de date Iteratii P95 Latență (ms) Inf2.xl Cost pe oră la cerere Inf2.xl (cost pe imagine)
Difuziune stabilă 1.5 512 × 512 bf16 50 2,427.4 $0.76 $0.0005125
Difuziune stabilă 1.5 768 × 768 bf16 50 8,235.9 $0.76 $0.0017387
Difuziune stabilă 1.5 512 × 512 bf16 30 1,456.5 $0.76 $0.0003075
Difuziune stabilă 1.5 768 × 768 bf16 30 4,941.6 $0.76 $0.0010432
Difuziune stabilă 2.1 512 × 512 bf16 50 1,976.9 $0.76 $0.0004174
Difuziune stabilă 2.1 768 × 768 bf16 50 6,836.3 $0.76 $0.0014432
Difuziune stabilă 2.1 512 × 512 bf16 30 1,186.2 $0.76 $0.0002504
Difuziune stabilă 2.1 768 × 768 bf16 30 4,101.8 $0.76 $0.0008659

Concluzie

În această postare, ne-am aprofundat în compilarea, optimizarea și implementarea modelului Stable Diffusion 2.1 folosind instanțe Inf2. De asemenea, am demonstrat implementarea modelelor Stable Diffusion folosind SageMaker. Instanțele Inf2 oferă, de asemenea, performanțe excelente de preț pentru Stable Diffusion 1.5. Pentru a afla mai multe despre motivul pentru care instanțele Inf2 sunt excelente pentru AI generativă și modele de limbaj mari, consultați Instanțele Amazon EC2 Inf2 pentru inferențe AI generative cu costuri reduse și de înaltă performanță sunt acum disponibile în general. Pentru detalii despre performanță, consultați Performanță Inf2. Consultați exemple suplimentare pe GitHub repo.

Mulțumiri speciale lui Matthew Mcclain, Beni Hegedus, Kamran Khan, Shruti Koparkar și Qing Lan pentru revizuirea și furnizarea de contribuții valoroase.


Despre Autori

Maximize Stable Diffusion performance and lower inference costs with AWS Inferentia2 | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.Vivek Gangasani este arhitect senior de soluții de învățare automată la Amazon Web Services. Lucrează cu startup-uri de învățare automată pentru a construi și a implementa aplicații AI/ML pe AWS. În prezent, el se concentrează pe furnizarea de soluții pentru MLOps, inferență ML și ML low-code. A lucrat la proiecte în diferite domenii, inclusiv procesarea limbajului natural și viziunea computerizată.

Maximize Stable Diffusion performance and lower inference costs with AWS Inferentia2 | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.KC Tung este arhitect senior de soluții în AWS Annapurna Labs. El este specializat în formarea și implementarea modelelor de învățare profundă la scară în cloud. Are un doctorat. în biofizică moleculară de la Universitatea din Texas Southwestern Medical Center din Dallas. A vorbit la AWS Summits și AWS Reinvent. Astăzi îi ajută pe clienți să antreneze și să implementeze modele mari PyTorch și TensorFlow în cloud AWS. Este autorul a două cărți: Aflați TensorFlow Enterprise și Referință TensorFlow 2 Pocket.

Maximize Stable Diffusion performance and lower inference costs with AWS Inferentia2 | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.Rupinder Grewal este un arhitect specializat în soluții Sr Ai/ML cu AWS. În prezent, se concentrează pe servirea modelelor și a MLOps-ului pe SageMaker. Înainte de acest rol, a lucrat ca inginer de învățare automată, construind și găzduind modele. În afara serviciului, îi place să joace tenis și să meargă cu bicicleta pe traseele montane.

Timestamp-ul:

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