Ehi a tutti, Nina qui, di nuovo su agntbox.com! Oggi voglio parlare di qualcosa che ha ronzato nei miei canali Slack e mi ha perseguitato durante le mie sessioni di codifica notturne: i framework di intelligenza artificiale. In particolare, voglio esplorare un angolo che viene spesso trascurato quando tutti sono occupati a inseguire l’ultimo LLM: gli strumenti per il fine-tuning e il deployment di modelli più piccoli e specializzati.
Tutti conosciamo i grandi nomi – TensorFlow, PyTorch. Sono i giganti, i giocatori affermati. E con buone ragioni! Sono incredibilmente potenti e versatili. Ma diciamocelo, a volte non hai bisogno di una corazzata per attraversare un lago. A volte, hai bisogno di un agile motoscafo, soprattutto quando stai lavorando con una scadenza stretta, un dataset specifico e un obiettivo chiaro in mente. È qui che voglio parlare di qualcosa con cui mi sono fatta piuttosto confortevole ultimamente: Hugging Face Optimum per ONNX Runtime.
Ora, prima che i vostri occhi si rivolgano verso il cielo con le sigle, lasciate che vi spieghi. Hugging Face, ovviamente, è il beniamino del mondo NLP, rendendo i modelli pre-addestrati accessibili a tutti. ONNX (Open Neural Network Exchange) è uno standard aperto per la rappresentazione dei modelli di apprendimento automatico, il che significa fondamentalmente che puoi convertire modelli addestrati in un framework (come PyTorch) e eseguirli in un altro (come TensorFlow, o nel nostro caso, ONNX Runtime). E ONNX Runtime? È il motore di inferenza ad alte prestazioni di Microsoft.
Quindi, cosa fa Hugging Face Optimum per ONNX Runtime? Fondamentalmente è un ponte, un ottimizzatore e un assistente al deployment tutto in uno. Ti aiuta a prendere i tuoi modelli di Hugging Face, ottimizzarli per l’inferenza ONNX Runtime e spesso, ottenere un significativo aumento di velocità senza sacrificare molto (o nessuna) accuratezza. E questo, miei amici, è oro.
Perché sono ossessionata da Optimum ONNX Runtime in questo momento
Il mio viaggio in Optimum ONNX Runtime è iniziato, come la maggior parte delle mie ossessioni tecnologiche, con un problema. Stavo lavorando a un progetto per un cliente che prevedeva il deployment di un modello BERT di dimensioni relativamente ridotte per la classificazione del testo sui ticket di supporto clienti. Il modello era stato addestrato su un dataset personalizzato e, mentre le sue prestazioni erano ottime, il tempo di inferenza era un po’ troppo lento per interazioni in tempo reale con i clienti. Parliamo di circa 150-200ms per inferenza su una potente GPU, il che non è terribile, ma per applicazioni in tempo reale ad alto volume, ogni millisecondo conta.
Ho provato tutti i soliti sospetti: batching, ottimizzazione delle pipeline di input, persino una base di quantizzazione. Abbiamo ottenuto alcuni miglioramenti, ma niente di drammatico. Poi, un collega ha menzionato Optimum e, in particolare, la sua integrazione con ONNX. Ero scettica all’inizio. Un altro livello di astrazione? Altre dipendenze? Ma ero disperata, quindi mi sono tuffata.
Quello che ho trovato è stato un flusso di lavoro sorprendentemente semplice che ha fornito risultati. Siamo riusciti a ridurre il nostro tempo di inferenza a circa 50-70ms per inferenza sulla stessa GPU e abbiamo visto anche prestazioni decenti su una CPU per compiti meno critici. Questo è un aumento di velocità 2-3 volte, che nel mondo dell’IA in tempo reale è un enorme successo. Significava che potevamo scalare il nostro servizio in modo molto più efficiente e fornire risposte più rapide ai clienti, impattando direttamente sulla loro esperienza.
Il problema che risolve: Prestazioni e Portabilità
Dobbiamo essere onesti, il deployment dei modelli di IA può essere un mal di testa. Alleni un bellissimo modello in PyTorch e poi devi capire come farlo girare in modo efficiente in produzione. A volte, sei bloccato con una configurazione hardware specifica, oppure devi distribuire su un dispositivo edge con risorse limitate. È qui che entra in gioco ONNX. Fornisce un formato comune, separando il tuo modello dal framework di addestramento.
Optimum porta questo un passo oltre. Non si tratta solo di convertire in ONNX; si tratta di ottimizzare quel modello ONNX. Può applicare tecniche come ottimizzazioni grafiche, fusione di operatori e persino quantizzare il tuo modello per ridurne le dimensioni e accelerare l’inferenza, spesso con un impatto minimo sull’accuratezza. Questo è particolarmente utile per modelli più piccoli o quando sei vincolato dalla memoria o dal calcolo sulla tua destinazione di deployment.
Il caso d’uso del mio cliente era un esempio perfetto. Avevamo un modello PyTorch, ma volevamo distribuirlo su un’istanza cloud con GPU NVIDIA, e avevamo bisogno del massimo throughput. Optimum ONNX Runtime ci ha permesso di esportare il modello, applicare ottimizzazioni specifiche per il nostro hardware target e farlo girare come un sogno.
Iniziare con Optimum ONNX Runtime: Un Esempio Pratico
Facciamo un passo indietro e seguiamo un esempio di base su come potresti usare Optimum per esportare e ottimizzare un modello di Hugging Face per ONNX Runtime. Per questo, useremo un semplice modello di analisi del sentiment.
Per prima cosa, dovrai installare le librerie necessarie:
pip install transformers optimum[onnxruntime] onnx
Ora, scriviamo un po’ di codice Python per esportare un modello di analisi sentimentale pre-addestrato.
Esempio 1: Esportazione di un Modello di Analisi del Sentiment
Qui, stiamo prendendo un modello standard `distilbert-base-uncased-finetuned-sst-2-english` e lo stiamo esportando in formato ONNX utilizzando Optimum.
from transformers import pipeline
from optimum.onnxruntime import ORTModelForSequenceClassification
from transformers import AutoTokenizer
model_id = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(model_id)
# Esporta il modello in ONNX
# La `save_directory` è dove il tuo modello ONNX e il tokenizer saranno salvati.
# `opset` specifica la versione dell'insieme degli operatori ONNX.
# `input_names` sono importanti per definire gli input al tuo grafo ONNX.
onnx_path = "./onnx_sentiment_model/"
ort_model = ORTModelForSequenceClassification.from_pretrained(model_id, from_transformers=True)
ort_model.save_pretrained(onnx_path)
tokenizer.save_pretrained(onnx_path)
print(f"Modello e tokenizer esportati in {onnx_path}")
Dopo aver eseguito questo, avrai una directory chiamata `onnx_sentiment_model` contenente il tuo file `model.onnx` e i file del tokenizer. Questo file `model.onnx` è la versione ottimizzata pronta per ONNX Runtime.
Esempio 2: Eseguire Inferenza con il Modello ONNX
Ora, carichiamo quel modello esportato ed eseguiamo alcune inferenze con esso. Nota come carichiamo `ORTModelForSequenceClassification` direttamente dal `onnx_path` che abbiamo salvato.
from optimum.onnxruntime import ORTModelForSequenceClassification
from transformers import AutoTokenizer, pipeline
import time
onnx_path = "./onnx_sentiment_model/"
# Carica il modello ONNX e il tokenizer
ort_model = ORTModelForSequenceClassification.from_pretrained(onnx_path)
tokenizer = AutoTokenizer.from_pretrained(onnx_path)
# Crea una pipeline per un'inferenza facile
onnx_pipeline = pipeline(
"sentiment-analysis",
model=ort_model,
tokenizer=tokenizer,
accelerator="ort" # Questo dice alla pipeline di usare ONNX Runtime
)
text_samples = [
"Adoro questo prodotto, è fantastico!",
"Questo film era solo ok, un po' noioso.",
"Odio assolutamente aspettare in lunghe file.",
"Il servizio era incredibilmente veloce ed efficiente."
]
print("\n--- Eseguendo Inferenza con il Modello ONNX ---")
start_time = time.time()
results = onnx_pipeline(text_samples)
end_time = time.time()
for i, res in enumerate(results):
print(f"Testo: '{text_samples[i]}' -> Etichetta: {res['label']}, Score: {res['score']:.4f}")
print(f"Tempo di inferenza per {len(text_samples)} campioni: {(end_time - start_time):.4f} secondi")
Quando esegui questo, vedrai le previsioni del sentiment. Più importante ancora, se dovessi confrontare il tempo di inferenza con il modello PyTorch originale sulla stessa hardware, probabilmente osserveresti un notevole aumento di velocità, soprattutto per lotti più grandi o modelli più complessi. Il parametro `accelerator=”ort”` nella pipeline è una piccola ma potente bandiera che dice a Hugging Face di usare ONNX Runtime per l’inferenza, ed è qui che accade la magia.
Esempio 3: Controllare le Funzionalità di Ottimizzazione (Facoltativo ma Potente)
Optimum consente un controllo dettagliato sul processo di ottimizzazione. Ad esempio, puoi specificare il livello di ottimizzazione o persino scegliere ottimizzazioni grafiche specifiche. Questo può essere cruciale quando cerchi di spremere ogni ultima goccia di prestazione dal tuo modello o quando hai bisogno di fare compromessi tra velocità e accuratezza (ad esempio, con la quantizzazione).
from optimum.onnxruntime import ORTModelForSequenceClassification, ORTConfig
from transformers import AutoTokenizer
from optimum.exporters.tasks import TasksManager
from optimum.onnxruntime.configuration import AutoOptimizationConfig
model_id = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(model_id)
# Definisci la configurazione di ottimizzazione
# Qui stiamo usando l'ottimizzazione predefinita, ma puoi ulteriormente personalizzarla
# Ad esempio, per applicare la quantizzazione: `optimization_config = AutoOptimizationConfig.O2()`
optimization_config = AutoOptimizationConfig.O1() # O1 per ottimizzazione di base, O2 per più aggressiva, O3 per completa
# Esporta il modello con configurazione di ottimizzazione esplicita
onnx_path_optimized = "./onnx_sentiment_model_optimized/"
task = TasksManager.get_task_from_model_or_model_name(model_id)
# Potresti dover regolare il parametro `feature` in base al compito del tuo modello
# Per la classificazione di sequenze, è spesso 'sequence-classification'
ORTModelForSequenceClassification.from_pretrained(
model_id,
from_transformers=True,
export_feature=task.feature,
optimization_config=optimization_config,
).save_pretrained(onnx_path_optimized)
tokenizer.save_pretrained(onnx_path_optimized)
print(f"Modello e tokenizer ottimizzati esportati in {onnx_path_optimized}")
L’`AutoOptimizationConfig` è il tuo alleato qui. `O1` fornisce ottimizzazioni di base del grafo, `O2` aggiunge fusioni più aggressive e eliminazioni di nodi, mentre `O3` include tutte le ottimizzazioni disponibili, inclusa la quantizzazione se applicabile. Scegliere il livello giusto dipende dalle tue esigenze specifiche e dall’hardware che stai mirando. Per il mio cliente, abbiamo sperimentato tra `O1` e `O2` per trovare il punto ottimale, inclinandoci verso `O2` per il miglior equilibrio tra velocità e accuratezza.
Le mie conclusioni e i prossimi passi
La mia esperienza con Hugging Face Optimum per ONNX Runtime è stata straordinariamente positiva. Non è una soluzione magica per ogni singolo progetto di IA, ma affronta un bisogno molto comune e critico: far funzionare i tuoi modelli più velocemente ed efficientemente in produzione, soprattutto quando lavori con i modelli di Hugging Face.
- Incremento delle prestazioni: Il beneficio principale è la significativa riduzione del tempo di inferenza. Per applicazioni in tempo reale, questo può rappresentare un cambiamento notevole, migliorando l’esperienza utente e riducendo i costi infrastrutturali.
- Portabilità: Convertendo in ONNX, i tuoi modelli diventano più portabili, eseguibili su hardware e sistemi operativi diversi senza essere legati a un framework di deep learning specifico.
- Facilità d’uso: L’integrazione con la libreria `transformers` di Hugging Face è straordinariamente fluida. Se sei già familiare con Hugging Face, la curva di apprendimento per Optimum è piuttosto dolce.
- Efficienza delle risorse: I modelli ottimizzati spesso richiedono meno memoria e cicli CPU/GPU, il che è cruciale per distribuzioni edge o ambienti cloud sensibili ai costi.
Una cosa che ho imparato è che vale la pena sperimentare con diversi livelli e configurazioni di ottimizzazione. Non accontentarti semplicemente del predefinito. Prova `O1`, `O2`, e anche `O3` (con quantizzazione se il tuo caso d’uso lo consente) e verifica i risultati sul tuo hardware target reale. I guadagni possono essere sorprendenti!
Guardando avanti, credo che strumenti come Hugging Face Optimum diventeranno ancora più essenziali. Man mano che i modelli di IA proliferano e si spostano in ambienti di distribuzione più diversificati, la capacità di ottimizzare e semplificare la loro inferenza sarà fondamentale. Sono particolarmente entusiasta di vedere come Optimum evolverà con i nuovi acceleratori hardware e tecniche di quantizzazione più avanzate.
Conclusioni pratiche per il tuo prossimo progetto di IA:
- Valuta le tue esigenze di inferenza: Prima di esplorare l’ottimizzazione, definisci chiaramente i tuoi requisiti di prestazione. Qual è una latenza accettabile? Qual è il tuo obiettivo di throughput?
- Considera l’ONNX da subito: Se stai utilizzando modelli di Hugging Face e le prestazioni sono una preoccupazione, inizia a pensare all’esportazione e all’ottimizzazione in ONNX durante il tuo ciclo di sviluppo, non solo in fase di distribuzione.
- Confronta, confronta, confronta: Misura sempre il reale miglioramento (o degrado) delle prestazioni dopo aver applicato le ottimizzazioni. Non fidarti dei guadagni teorici. Utilizza dati reali e hardware reale.
- Sperimenta con i livelli di ottimizzazione: Non limitarti a utilizzare le impostazioni predefinite. Sperimenta con `AutoOptimizationConfig.O1()`, `O2()`, e `O3()` per trovare il miglior equilibrio per il tuo modello e caso d’uso.
- Tieniti aggiornato: La libreria Hugging Face Optimum è in continua evoluzione. Tieni d’occhio le loro versioni e la documentazione per nuove funzionalità e miglioramenti delle prestazioni.
Questo è tutto per me questa settimana! Se hai avuto difficoltà con le prestazioni del deployment del modello, prova Hugging Face Optimum per ONNX Runtime. Potrebbe essere proprio il propulsore di cui hai bisogno. Fammi sapere nei commenti se lo hai usato o se hai altri strumenti preferiti per l’ottimizzazione dei modelli. Buona inferenza!
🕒 Published: