Utilizzo di librerie open source per reti neurali in C++
Introduzione
L'intelligenza artificiale moderna spesso entra in un'azienda attraverso Python, notebook, ambienti demo e la comprensibile eccitazione di vedere un modello funzionare per la prima volta. Quella fase è reale, utile e anche un po’ magica. È dove la curiosità è economica e l'iterazione è veloce. Ma la vita di un prodotto reale non finisce con la demo. Un modello che deve servire i clienti, adattarsi a un backend, funzionare su hardware di fabbrica, vivere all'interno di un prodotto desktop o sopravvivere a condizioni di rete inadeguate non è più solo un modello. Diventa un componente di un sistema e i sistemi sono il luogo in cui la maturità ingegneristica inizia a contare.
Questo è il momento in cui il C++ ritorna nella stanza. Non perché gli ingegneri siano sentimentali nei confronti del passato, e non perché ogni problema dell’intelligenza artificiale dovrebbe diventare nativo, ma perché la produzione pone domande che la sperimentazione di livello superiore può rinviare solo per un certo periodo. Di quanta memoria ha realmente bisogno il processo? Qual è la latenza in stato stazionario sotto carico? Il tempo di avvio può sopravvivere alla scalabilità automatica? Il runtime può vivere all'interno di un'applicazione nativa esistente? Possiamo fornire lo stesso percorso di inferenza a un server, a un edge box e a una workstation dell'operatore senza ricostruire l'intero prodotto attorno a uno stack di ricerca?
Le librerie open source sono ciò che rende possibile questa transizione senza cedere il controllo a una scatola nera del fornitore. Ci forniscono tempi di esecuzione stabili, astrazioni tensoriali, kernel ottimizzati, percorsi di esecuzione quantizzati, backend compatibili con l'hardware e, nella recente era LLM, motori di inferenza locale sorprendentemente capaci. Ma anche l’abbondanza di biblioteche può rendere il panorama confuso. Gli ingegneri spesso chiedono quale sia la biblioteca migliore quando la domanda migliore è quale biblioteca sia onesta riguardo al lavoro che abbiamo davanti.
Questo articolo prende quel percorso più radicato. Considereremo le principali librerie rilevanti per il C++ nell'intelligenza artificiale non come distintivi di identità ma come personalità ingegneristiche con punti di forza, punti ciechi e presupposti operativi. Alla fine, l'obiettivo non è semplicemente conoscere i nomi ONNX Runtime, LibTorch, oneDNN, OpenVINO, TensorFlow Lite e llama.cpp. L'obiettivo è capire quando ognuno aiuta, quando diventa troppo pesante, quando diventa troppo stretto, e come scegliere senza lasciarsi condizionare dalle mode.
Perché i sistemi di intelligenza artificiale continuano a tornare al C++
C'è un ritmo nella distribuzione dell'intelligenza artificiale che vale la pena nominare chiaramente, perché una volta osservato, molte scelte architettoniche diventano più facili da comprendere. Innanzitutto c’è la fase di scoperta. I ricercatori e gli ingegneri di prodotto stanno ancora imparando cosa può fare il modello, di quali dati ha bisogno e dove potrebbe effettivamente risiedere il valore. In quella fase, l’espressività batte la disciplina. Sperimentazione rapida, ricchi strumenti Python e framework di ricerca flessibili sono esattamente ciò di cui il team ha bisogno.
Poi arriva la seconda fase, meno affascinante, in cui un prototipo inizia ad accumulare obblighi. Un team di supporto deve comprendere i fallimenti. Un team SRE desidera un comportamento di avvio e memoria prevedibile. La finanza vuole sapere se il conto del servizio è un picco temporaneo o una perdita permanente. Un cliente incorporato chiede se il modello può essere eseguito offline. Una revisione della sicurezza chiede cosa viene fornito esattamente all'interno del codice binario e quali parti possono essere controllate. All'improvviso il modello smette di essere un artefatto di ricerca e diventa cittadino di un ambiente di produzione.
Il C++ continua a tornare a quel punto perché consente all'ingegneria di rispondere a domande concrete invece di agitarle con la mano. Un servizio nativo può controllare le strategie di allocazione, i pool di thread, i limiti ABI, il packaging, le ottimizzazioni specifiche della CPU e l'integrazione con i sottosistemi esistenti sensibili alle prestazioni. Questo controllo non è sempre necessario. Ma dove è necessario, è molto difficile fingere con la retorica.
Un utile controesempio aiuta qui. Se il tuo team sta costruendo un classificatore di documenti interno con un carico leggero che viene eseguito una volta ogni ora, il percorso di minor resistenza potrebbe essere un servizio Python con un framework di servizio stabile e pochissimo codice nativo. There is nothing shameful about that. D'altra parte, se lo stesso team incorpora l'inferenza all'interno di un'applicazione desktop C++ sensibile alla latenza, la spedisce a un dispositivo edge con risorse limitate o inserisce l'esecuzione del modello direttamente in un percorso backend attivo, fingere che il linguaggio di runtime non abbia importanza diventa molto rapidamente costoso. In altre parole, il C++ non è la risposta a tutti i problemi dell’intelligenza artificiale, ma rimane una delle risposte più serie ogni volta che il sistema stesso diventa il problema.
Le biblioteche come personalità dell'ingegneria
Il modo più semplice per perdersi in questo ecosistema è trattare ogni biblioteca come se competesse per lo stesso lavoro. Non lo sono. Un framework orientato alla formazione, un runtime di inferenza portatile, una libreria del kernel e un motore LLM locale risolvono tutti problemi diversi. Se li comprimiamo in una categoria chiamata librerie AI, finiamo per fare scelte basate sulla familiarità del marchio piuttosto che sulla progettazione del sistema.
ONNX Runtime è, in molti ambienti di produzione, la scelta più disciplinata e meno teatrale. Si basa su una promessa chiara: esportare il modello in un formato stabile, caricarlo tramite un runtime incentrato sull'esecuzione e lasciare che l'applicazione sia proprietaria del resto del sistema. Sembra semplice, e la semplicità è esattamente il motivo per cui è potente. ONNX Runtime è spesso la risposta giusta quando la fase di ricerca è già avvenuta altrove e ciò che rimane è il sobrio lavoro di servire l'inferenza ripetutamente, in modo portabile e con un comportamento operativo prevedibile. Un backend di visione artificiale che riceve immagini, normalizza tensori, esegue un grafico noto e restituisce risultati a un servizio C++ esistente è una storia ideale di ONNX Runtime. Una soluzione inadeguata sarebbe un prodotto il cui valore fondamentale dipende dal comportamento dinamico del tempo di addestramento, da frequenti interventi chirurgici sui grafici all'interno dell'applicazione o da un insieme in continua evoluzione di operatori personalizzati che rendono fragile l'esportazione. In tal caso, il confine del runtime che inizialmente sembrava pulito può diventare una fonte di attrito.
LibTorch ha un carattere diverso. Non si tratta principalmente di un limite di esecuzione leggero. È il volto C++ di un framework completo di deep learning. Ciò lo rende più pesante, ma lo rende anche più espressivo. Quando un'applicazione nativa ha veramente bisogno di possedere operazioni tensoriali, creare modelli, eseguire manipolazioni di tipo training o restare vicina alla semantica PyTorch durante lo sviluppo e la produzione, LibTorch diventa più avvincente di ONNX Runtime. C'è una certa onestà nello sceglierlo quando il prodotto necessita realmente di un framework e non solo di un runtime. Il controesempio è altrettanto importante. I team a volte adottano LibTorch per una semplice inferenza statica perché sembra prestigioso o a prova di futuro. Poi scoprono di aver importato una superficie concettuale e operativa molto più ampia del carico di lavoro richiesto. Un piccolo servizio di inferenza che necessita solo di caricare un modello grafico stabile può pagare per tale decisione in termini di dimensioni del pacchetto, complessità e impegno di debug.
oneDNN e OpenVINO vivono più vicini al metal e premiano una mentalità più attenta alle prestazioni. oneDNN di solito non è la libreria a cui ti rivolgi perché desideri una storia completa del prodotto. È la libreria che apprezzi quando i kernel della CPU, i formati di memoria e l'efficienza a livello di operatore diventano abbastanza importanti da meritare un'attenzione diretta. Molti team lo utilizzano indirettamente attraverso runtime di livello superiore, il che spesso è saggio. OpenVINO, nel frattempo, si trova in una posizione più strategica. Aiuta i team che si preoccupano della distribuzione orientata a Intel, dell'ottimizzazione dei grafici e dell'esecuzione basata sull'hardware senza voler gestire manualmente ogni dettaglio di basso livello. In pratica, questi strumenti iniziano ad avere importanza quando il problema aziendale non è più semplicemente "eseguire il modello" ma "eseguire il modello in modo efficiente sull'hardware che possiamo effettivamente acquistare, implementare e mantenere". Questa distinzione sembra piccola in una riunione e diventa molto grande in un budget.
TensorFlow Lite rappresenta un temperamento completamente diverso. È la voce della moderazione. Sui dispositivi edge, sugli obiettivi mobili e sui sistemi con risorse limitate, la completezza è spesso meno preziosa della forma fisica. Gli ingegneri non hanno bisogno di una struttura maestosa lì; hanno bisogno di un modello che carichi, esegua e rispetti i rigidi vincoli relativi a memoria, dimensioni del pacchetto, consumo energetico e tempo di avvio. TensorFlow Lite ha senso quando l'obiettivo stesso della distribuzione è la forza principale che modella l'architettura. Anche il controesempio è comune: un team inizia con un edge runtime perché sembra efficiente, quindi lo estende lentamente a una piattaforma server più ampia o a un flusso di lavoro con esigenze più dinamiche di quelle per cui è stato creato. L’efficienza ai margini non si traduce automaticamente in comfort ovunque.
Poi c'è llama.cpp, che merita un'attenzione particolare perché ha cambiato la mappa emotiva dell'inferenza locale. Prima che llama.cpp e progetti simili diventassero mainstream, molti ingegneri presumevano che il servizio di modelli linguistici locali di grandi dimensioni sarebbe rimasto un giocattolo di ricerca o un'appliance aziendale. llama.cpp ha dimostrato qualcosa di più interessante: con una quantizzazione aggressiva, un attento lavoro sul kernel e un'ingegneria disciplinata, un moderno LLM potrebbe diventare un componente nativo locale all'interno dei sistemi ordinari. Questa intuizione conta al di là di un singolo progetto. Ha ricordato all’intero settore che l’esecuzione nativa, la compressione del modello e l’implementazione pratica possono muoversi molto più velocemente di quanto spesso suggeriscano le narrazioni centralizzate. Ma anche llama.cpp ha un confine naturale. È eccellente quando il lavoro esegue modelli di trasformatori supportati localmente ed in modo efficiente. Non è un sostituto generale dell’intero ecosistema del deep learning e i team si mettono nei guai quando chiedono di diventarlo.
Come scegliere senza lasciarsi sedurre dall'hype
Il modo più affidabile per scegliere tra queste librerie è iniziare con il prodotto e solo successivamente nominare lo strumento. Inizia chiedendoti cosa possiede veramente la tua applicazione e cosa consuma semplicemente. Se il sistema utilizza principalmente un modello stabile e necessita di inferenza portabile e ben delimitata, ONNX Runtime è spesso la risposta più tranquilla. Se il sistema stesso deve parlare il linguaggio dei tensori, dei moduli e della semantica del framework, LibTorch merita la discussione. Se l’efficienza della CPU, l’ottimizzazione dei grafici o l’implementazione pesante di Intel sono la parte difficile, oneDNN e OpenVINO si avvicinano al centro. Se l'obiettivo è piccolo, offline, sensibile alla batteria o integrato, TensorFlow Lite diventa più naturale. Se il prodotto riguarda esplicitamente l'esecuzione di un modello linguistico quantizzato locale in un ambiente nativo, llama.cpp appartiene presto al tavolo.
Una seconda domanda è altrettanto importante: dove verranno effettivamente ripagate le spese ingegneristiche? I team spesso scelgono le biblioteche in base ai titoli dei benchmark e poi scoprono che il loro vero problema è altrove. Un runtime con numeri di throughput spettacolari potrebbe comunque essere la soluzione sbagliata se l'esportazione è instabile, la preelaborazione è disordinata o il pacchetto di distribuzione diventa fragile. Un runtime leggermente più lento potrebbe comunque rappresentare la scelta aziendale migliore se crea un confine più netto tra produttori di modelli e manutentori del sistema. Gli ingegneri che hanno distribuito più di un prodotto AI imparano profondamente questa lezione: la migliore libreria non è sempre quella che vince la classifica dei benchmark, ma quella che rende più facile ragionare sull'intero sistema alle due del mattino.
È qui che i controesempi diventano salutari. Considera un team che crea un servizio di analisi di documenti nativi. La scelta più alla moda potrebbe essere quella di ricorrere alla struttura più pesante disponibile, perché sembra a prova di futuro. Ma se il modello è statico, la pipeline di preelaborazione è semplice e la reale esigenza è un'inferenza stabile all'interno di un servizio C++ esistente, è probabile che ONNX Runtime crei meno trascinamenti a lungo termine. Consideriamo ora il contrario. Un team sta eseguendo sperimentazioni native con flussi di tensori personalizzati, frequenti modifiche all'architettura e uno stretto accoppiamento con la logica di addestramento basata su PyTorch. Forzare tutto attraverso ONNX perché sembra "pronto per la produzione" può creare un fragile flusso di lavoro incentrato sull'esportazione di cui nessuno gode veramente. In ogni caso l'errore è lo stesso: il team ha scelto un'identità prima di scegliere un carico di lavoro.
Come si presenta effettivamente una buona integrazione
Un flusso di lavoro di integrazione maturo inizia con il contratto dati, non con la libreria. Prima di discutere sui tempi di esecuzione, decidere cosa fornisce l'applicazione al modello e cosa restituisce il modello all'applicazione. Assegnare un nome alle forme del tensore, ai dtype, alle regole di normalizzazione, ai percorsi di tokenizzazione, al comportamento di riempimento, alle ipotesi di batching e alle condizioni di errore. Sembra quasi burocratico, ma è la fonte silenziosa di molte implementazioni di successo. I sistemi falliscono non solo perché i tempi di esecuzione sono sbagliati, ma perché i confini attorno ad essi sono nebbiosi.
Una volta che il contratto dati è stabile, l'esportazione o il confezionamento del modello diventa molto più semplice da convalidare. Un team può confrontare i risultati del percorso di ricerca e del percorso di produzione in base a input rappresentativi, misurare le tolleranze e rilevare dove si sposta la fedeltà. È qui che gli ingegneri scoprono se la loro elegante architettura sopravvive alla realtà. A volte il grafico esportato va bene e l'unico problema è la preelaborazione non corrispondente. A volte il runtime è impeccabile e il vero problema è l'eccesso di sottoscrizioni ai thread in altre parti del servizio. A volte un modello apparentemente piccolo non può sopravvivere alla pressione della memoria della concorrenza reale. Ognuna di queste scoperte è utile. Significa che il sistema ha iniziato a diventare visibile.
Dopodiché arrivano il benchmarking e la profilazione, e qui si applica la stessa vecchia regola: misura il sistema che intendi spedire, non il giocattolo con cui ti sentivi intelligente. Confronta il modello in base a forme di richiesta realistiche, dimensioni dei batch, variabilità dell'input e condizioni hardware. Anche la pre-elaborazione e la post-elaborazione del profilo, perché molti team inconsciamente confrontano solo il nucleo del modello e dimenticano che i clienti pagano per l'intero percorso. Nell’intelligenza artificiale di produzione, un grafico di dieci millisecondi circondato da sessanta millisecondi di colla evitabile è ancora una caratteristica di settanta millisecondi.
Infine, rendere riproducibile la distribuzione. Gli stack IA nativi premiano la disciplina. Le versioni dei pin, i presupposti del compilatore e del runtime del documento, decidono quali provider di esecuzione o funzionalità della CPU sono richiesti e mantengono un insieme ristretto di configurazioni supportate. Se un compagno di squadra non riesce a riprodurre lo stesso percorso di inferenza su un'altra macchina senza archeologia, lo stack non è pronto, per quanto impressionante possa essere stata la demo. Una buona ingegneria dell'intelligenza artificiale C++ non è solo una questione di velocità. Si tratta di rendere il sistema abbastanza calmo da far sì che la velocità rimanga comprensibile.
Errori che continuano a ripetersi
L’errore più comune è confondere la verità della ricerca con la verità della produzione. Un modello che sembra eccellente su un notebook può diventare scomodo una volta esportato, quantizzato, incorporato, osservato ed eseguito in concorrenza reale. Ciò non significa che il modello fosse pessimo. Significa che il sistema era più grande dell'esperimento. Il secondo errore ricorrente è fingere che la preelaborazione e la postelaborazione siano secondarie. Nei prodotti reali spesso rappresentano metà del lavoro. La politica di ridimensionamento delle immagini, il comportamento del tokenizzatore, la normalizzazione delle funzionalità, le soglie di calibrazione e la decodifica dell'output sono tutte correttezza e latenza della forma con la stessa certezza del runtime principale.
Un terzo errore è impegnarsi eccessivamente in un framework perché sembra moderno o completo. A volte gli ingegneri selezionano lo strumento più grande possibile in previsione di esigenze che non arrivano mai. Il prodotto paga quindi per le funzionalità che non utilizza. Esiste anche l'errore opposto: scegliere il runtime più leggero in nome della purezza e poi scoprire che il comportamento dinamico, le operazioni personalizzate o la semantica a livello di framework non erano dopo tutto opzionali. La saggezza sta nel pagare solo per il potere che puoi effettivamente spiegare.
C'è anche un fallimento di atteggiamento più sottile. Alcuni team trattano la scelta della libreria come se risolvesse l'intera questione ingegneristica. Non è così. I buoni risultati derivano da un lavoro ripetuto e umile: convalida degli output, misurazione dei percorsi critici, rimozione delle copie evitabili, riduzione degli attriti all'avvio, semplificazione del packaging e mantenimento dei limiti di runtime leggibili. Le librerie open source rendono possibile questo lavoro; non lo eseguono per nostro conto.
Una piccola storia di distribuzione che vale la pena ricordare
Immagina un team che inizia con un prototipo di visione Python. La demo è sufficientemente potente da ottenere il supporto interno e presto la conversazione si sposta sull'integrazione con un servizio C++ esistente che già gestisce l'inserimento di immagini, la valutazione delle regole e il reporting. La squadra ha diverse tentazioni. Uno è mantenere il modello dietro un servizio Python separato per sempre perché è facile a breve termine. Un altro è spostare immediatamente tutto in una struttura nativa dei pesi massimi perché sembra una cosa seria. Un terzo è passare settimane a discutere di architettura prima di stabilizzare anche il contratto di input.
Il percorso più maturo è più tranquillo. Innanzitutto il team definisce attentamente la preelaborazione e la semantica dell'output. Quindi verifica la fedeltà dell'esportazione su immagini rappresentative. Sceglie ONNX Runtime perché il problema è l'inferenza statica e non la sperimentazione basata sul framework. Successivamente, per una variante edge con vincoli hardware più severi, valuta se TensorFlow Lite o un percorso di runtime ottimizzato in modo più aggressivo ha senso per quel ramo di prodotto. Mesi dopo, se l'azienda aggiunge una funzionalità di assistente locale, anche llama.cpp potrebbe entrare nell'architettura, non perché una libreria abbia vinto l'intero dibattito, ma perché ogni strumento si è guadagnato il suo posto in un angolo diverso del sistema.
Questa è la lezione più profonda dietro tutte queste biblioteche. Una seria ingegneria dell’intelligenza artificiale raramente premia la purezza. Premia la forma fisica. La migliore libreria open source non è quella con il seguito più seguito. È quello che permette al tuo modello di diventare parte di un sistema reale senza forzare il resto del sistema a diventare irragionevole.
Laboratorio pratico: crea una piccola CLI ONNX Runtime
La teoria diventa più convincente quando viene compilata.
Costruiamo il più piccolo programma di inferenza nativo utile in C++. L'obiettivo non è addestrare un modello. L'obiettivo è provare con le proprie mani come si presenta un confine di runtime nativo.
Per questo esercizio ti serve:
- un compilatore C++17
- CMake
- un pacchetto ONNX Runtime predefinito dalle versioni ufficiali
- qualsiasi piccolo modello
.onnxil cui input è un tensore flat float
Disposizione del progetto
tiny-ort/
CMakeLists.txt
main.cpp
third_party/
onnxruntime/
model.onnx
__CODICE_0__
cmake_minimum_required(VERSION 3.16)
project(tiny_ort LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(ORT_ROOT "${CMAKE_SOURCE_DIR}/third_party/onnxruntime")
add_executable(tiny_ort main.cpp)
target_include_directories(tiny_ort PRIVATE "${ORT_ROOT}/include")
if (WIN32)
target_link_directories(tiny_ort PRIVATE "${ORT_ROOT}/lib")
target_link_libraries(tiny_ort PRIVATE onnxruntime)
else()
target_link_directories(tiny_ort PRIVATE "${ORT_ROOT}/lib")
target_link_libraries(tiny_ort PRIVATE onnxruntime)
endif()
__CODICE_0__
#include <onnxruntime_cxx_api.h>
#include <array>
#include <iostream>
#include <numeric>
#include <vector>
int main() {
Ort::Env env{ORT_LOGGING_LEVEL_WARNING, "tiny-ort"};
Ort::SessionOptions opts;
opts.SetIntraOpNumThreads(1);
opts.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
const ORTCHAR_T* model_path = ORT_TSTR("model.onnx");
Ort::Session session{env, model_path, opts};
std::vector<int64_t> shape{1, 4};
std::vector<float> input{0.25f, 0.50f, 0.75f, 1.0f};
auto mem_info = Ort::MemoryInfo::CreateCpu(
OrtArenaAllocator,
OrtMemTypeDefault
);
Ort::Value tensor = Ort::Value::CreateTensor<float>(
mem_info,
input.data(),
input.size(),
shape.data(),
shape.size()
);
const char* input_names[] = {"input"};
const char* output_names[] = {"output"};
auto outputs = session.Run(
Ort::RunOptions{nullptr},
input_names,
&tensor,
1,
output_names,
1
);
float* out = outputs[0].GetTensorMutableData<float>();
auto out_shape = outputs[0].GetTensorTypeAndShapeInfo().GetShape();
auto out_count = std::accumulate(
out_shape.begin(),
out_shape.end(),
int64_t{1},
std::multiplies<int64_t>{}
);
std::cout << "Output values:\n";
for (int64_t i = 0; i < out_count; ++i) {
std::cout << " [" << i << "] = " << out[i] << "\n";
}
return 0;
}
Costruire
Su Linux o macOS:
cmake -S . -B build
cmake --build build -j
./build/tiny_ort
Su Windows con MSVC:
cmake -S . -B build
cmake --build build --config Release
.\build\Release\tiny_ort.exe
Cosa ti insegna questo
Questo minuscolo progetto ti costringe già a confrontarti con diverse realtà produttive:
- dove vive il runtime
- come vengono impacchettate le dipendenze native
- quali sono effettivamente i nomi e le forme dei tensori
- come si sente la gestione esplicita della memoria in un confine di inferenza nativa
Questo è esattamente il punto. Una libreria smette di essere un termine di marketing e diventa una scelta ingegneristica.
Attività di prova per appassionati
Se desideri trasformare l'articolo in un laboratorio del fine settimana, ecco alcuni utili passaggi successivi:
- Sostituisci il vettore di input hardcoded con valori caricati da un piccolo file di testo o binario.
- Stampa le forme tensoriali di input e output in modo dinamico invece di assumerle.
- Aggiungi una semplice misurazione della latenza attorno a
session.Rune confronta i thread intra-operatori1,2e4. - Scambia ONNX Runtime per LibTorch in un'app di inferenza giocattolo simile e scrivi cosa è diventato più facile e cosa è diventato più pesante.
- Esporta un piccolo modello da Python, caricalo in questo programma C++ e verifica che le differenze di preelaborazione non modifichino silenziosamente il risultato.
Se svolgi queste cinque attività onestamente, capirai di più sull'implementazione dell'intelligenza artificiale rispetto a molte persone che possono recitare i nomi dei framework per un'ora.
Riepilogo
Le librerie di reti neurali open source per C++ non marciano in una parata. Sono nati da diverse esigenze ingegneristiche e rimangono molto utili quando rispettiamo quelle origini. ONNX Runtime è potente perché restringe il problema e offre ai team di produzione un limite di inferenza stabile. LibTorch è utile quando l'applicazione nativa ha veramente bisogno di pensare in tensori e moduli invece di limitarsi a consumare un grafico congelato. oneDNN e OpenVINO contano quando l'efficienza di basso livello e l'implementazione su famiglie hardware specifiche smettono di essere preoccupazioni secondarie. TensorFlow Lite brilla quando il dispositivo stesso è il vincolo difficile. llama.cpp è importante perché ha dimostrato, in modo molto pubblico, che un'attenta ingegneria nativa può trasformare i modelli linguistici moderni in componenti locali pratici piuttosto che in servizi distanti.
La scelta migliore è quindi raramente quella più alla moda. È quello che rende più calmo l’intero sistema. Un buon runtime è un runtime che il tuo team può comprendere, confrontare, profilare, pacchettizzare, testare e utilizzare senza mitologia. Quando gli ingegneri scelgono da quel luogo, l’intelligenza artificiale open source smette di sembrare uno zoo confuso di framework e inizia a sembrare quello che è realmente: una cassetta degli attrezzi abbastanza ricca da supportare prodotti nativi seri.
Riferimenti
- API C/C++ di runtime ONNX: https://onnxruntime.ai/docs/api/c/index.html
- Progetto ufficiale ONNX: https://onnx.ai/
- Documentazione del frontend PyTorch C++: https://docs.pytorch.org/cppdocs/frontend.html
- Documentazione ufficiale oneDNN: https://uxlfoundation.github.io/oneDNN/
- Documentazione OpenVINO: https://docs.openvino.ai/
- Documenti API LiteRT/TensorFlow Lite C++: https://ai.google.dev/edge/litert/api_docs/cc
- Repository lama.cpp: https://github.com/ggml-org/llama.cpp
- Repository GitHub di ONNX Runtime: https://github.com/microsoft/onnxruntime
- Repository PyTorch: https://github.com/pytorch/pytorch