TensorFlow.js funziona nel browser e in Node.js e in entrambe le piattaforme sono disponibili molte diverse configurazioni. Ciascuna piattaforma presenta una serie di considerazioni uniche che influenzeranno il modo in cui vengono sviluppate le applicazioni.
Nel browser, TensorFlow.js supporta dispositivi mobili e dispositivi desktop. Ogni dispositivo ha una serie specifica di vincoli, come le API WebGL disponibili, che vengono determinate e configurate automaticamente per te.
In Node.js, TensorFlow.js supporta l'associazione diretta all'API TensorFlow o l'esecuzione con le implementazioni più lente della CPU Vanilla.
Ambienti
Quando viene eseguito un programma TensorFlow.js, la configurazione specifica viene chiamata ambiente. L'ambiente è composto da un singolo backend globale e da una serie di flag che controllano le funzionalità granulari di TensorFlow.js.
Backend
TensorFlow.js supporta più backend diversi che implementano l'archiviazione di tensori e operazioni matematiche. In ogni momento è attivo un solo backend. Nella maggior parte dei casi, TensorFlow.js sceglierà automaticamente il backend migliore per te in base all'ambiente attuale. Tuttavia, a volte è importante sapere quale backend viene utilizzato e come cambiarlo.
Per scoprire quale backend stai utilizzando:
console.log(tf.getBackend());
Se desideri modificare manualmente il backend:
tf.setBackend('cpu');
console.log(tf.getBackend());
Back-end WebGL
Il backend WebGL, "webgl", è attualmente il backend più potente per il browser. Questo backend è fino a 100 volte più veloce del backend CPU vanilla. I tensori vengono archiviati come trame WebGL e le operazioni matematiche vengono implementate negli shader WebGL. Ecco alcune cose utili da sapere quando si utilizza questo backend: \
Evitare di bloccare il thread dell'interfaccia utente
Quando viene chiamata un'operazione, come tf.matMul(a, b), il risultante tf.Tensor viene restituito in modo sincrono, tuttavia il calcolo della moltiplicazione della matrice potrebbe non essere ancora pronto. Ciò significa che il tf.Tensor restituito è solo un handle per il calcolo. Quando chiami x.data()
o x.array()
, i valori verranno risolti quando il calcolo sarà effettivamente completato. Ciò rende importante utilizzare i metodi asincroni x.data()
e x.array()
rispetto alle loro controparti sincrone x.dataSync()
e x.arraySync()
per evitare di bloccare il thread dell'interfaccia utente durante il completamento del calcolo.
Gestione della memoria
Un avvertimento quando si utilizza il backend WebGL è la necessità di una gestione esplicita della memoria. WebGLTextures, che è il luogo in cui vengono infine archiviati i dati di Tensor, non vengono automaticamente raccolti dai rifiuti dal browser.
Per distruggere la memoria di un tf.Tensor
, puoi utilizzare il metodo dispose()
:
const a = tf.tensor([[1, 2], [3, 4]]);
a.dispose();
È molto comune concatenare più operazioni insieme in un'applicazione. Mantenere un riferimento a tutte le variabili intermedie per eliminarle può ridurre la leggibilità del codice. Per risolvere questo problema, TensorFlow.js fornisce un metodo tf.tidy()
che ripulisce tutti i tf.Tensor
che non vengono restituiti da una funzione dopo averla eseguita, in modo simile al modo in cui le variabili locali vengono ripulite quando viene eseguita una funzione:
const a = tf.tensor([[1, 2], [3, 4]]);
const y = tf.tidy(() => {
const result = a.square().log().neg();
return result;
});
Precisione
Sui dispositivi mobili, WebGL potrebbe supportare solo texture a virgola mobile a 16 bit. Tuttavia, la maggior parte dei modelli di machine learning vengono addestrati con pesi e attivazioni in virgola mobile a 32 bit. Ciò può causare problemi di precisione durante il porting di un modello per un dispositivo mobile poiché i numeri mobili a 16 bit possono rappresentare solo numeri nell'intervallo [0.000000059605, 65504]
. Ciò significa che dovresti fare attenzione che i pesi e le attivazioni nel tuo modello non superino questo intervallo. Per verificare se il dispositivo supporta texture a 32 bit, controlla il valore di tf.ENV.getBool('WEBGL_RENDER_FLOAT32_CAPABLE')
, se questo è falso, il dispositivo supporta solo texture a virgola mobile a 16 bit. Puoi utilizzare tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED')
per verificare se TensorFlow.js sta attualmente utilizzando texture a 32 bit.
Compilazione di shader e caricamenti di texture
TensorFlow.js esegue operazioni sulla GPU eseguendo programmi shader WebGL. Questi shader vengono assemblati e compilati pigramente quando l'utente chiede di eseguire un'operazione. La compilazione di uno shader avviene sulla CPU nel thread principale e può essere lenta. TensorFlow.js memorizzerà automaticamente nella cache gli shader compilati, rendendo molto più veloce la seconda chiamata alla stessa operazione con tensori di input e output della stessa forma. In genere, le applicazioni TensorFlow.js utilizzeranno le stesse operazioni più volte nel corso della vita dell'applicazione, quindi il secondo passaggio attraverso un modello di machine learning è molto più veloce.
TensorFlow.js memorizza anche i dati tf.Tensor come WebGLTextures. Quando viene creato un tf.Tensor
, non carichiamo immediatamente i dati sulla GPU, ma manteniamo i dati sulla CPU finché il tf.Tensor
non viene utilizzato in un'operazione. Se il tf.Tensor
viene utilizzato una seconda volta, i dati sono già sulla GPU quindi non ci sono costi di upload. In un tipico modello di machine learning, ciò significa che i pesi vengono caricati durante la prima previsione attraverso il modello e il secondo passaggio attraverso il modello sarà molto più veloce.
Se ti interessano le prestazioni della prima previsione tramite il tuo modello o il codice TensorFlow.js, ti consigliamo di riscaldare il modello passando un tensore di input della stessa forma prima che vengano utilizzati i dati reali.
Per esempio:
const model = await tf.loadLayersModel(modelUrl);
// Warmup the model before using real data.
const warmupResult = model.predict(tf.zeros(inputShape));
warmupResult.dataSync();
warmupResult.dispose();
// The second predict() will be much faster
const result = model.predict(userData);
Backend TensorFlow di Node.js
Nel backend TensorFlow Node.js, "node", l'API TensorFlow C viene utilizzata per accelerare le operazioni. Verrà utilizzata l'accelerazione hardware disponibile della macchina, come CUDA, se disponibile.
In questo backend, proprio come il backend WebGL, le operazioni restituiscono tf.Tensor
in modo sincrono. Tuttavia, a differenza del backend WebGL, l'operazione viene completata prima di recuperare il tensore. Ciò significa che una chiamata a tf.matMul(a, b)
bloccherà il thread dell'interfaccia utente.
Per questo motivo, se intendi utilizzarlo in un'applicazione di produzione, dovresti eseguire TensorFlow.js nei thread di lavoro per non bloccare il thread principale.
Per ulteriori informazioni su Node.js, consulta questa guida.
Back-end WASM
TensorFlow.js fornisce un backend WebAssembly ( wasm
), che offre l'accelerazione della CPU e può essere utilizzato come alternativa ai backend vanilla JavaScript CPU ( cpu
) e WebGL accelerati ( webgl
). Per usarlo:
// Set the backend to WASM and wait for the module to be ready.
tf.setBackend('wasm');
tf.ready().then(() => {...});
Se il tuo server fornisce il file .wasm
su un percorso diverso o con un nome diverso, utilizza setWasmPath
prima di inizializzare il backend. Consulta la sezione "Utilizzo dei bundler" nel file README per ulteriori informazioni:
import {setWasmPath} from '@tensorflow/tfjs-backend-wasm';
setWasmPath(yourCustomPath);
tf.setBackend('wasm');
tf.ready().then(() => {...});
Perchè WASM?
WASM è stato introdotto nel 2015 come un nuovo formato binario basato sul web, fornendo programmi scritti in JavaScript, C, C++, ecc. un obiettivo di compilazione per l'esecuzione sul web. WASM è supportato da Chrome, Safari, Firefox ed Edge dal 2017 ed è supportato dal 90% dei dispositivi in tutto il mondo.
Prestazione
Il backend WASM sfrutta la libreria XNNPACK per l'implementazione ottimizzata degli operatori di rete neurale.
Rispetto a JavaScript : i binari WASM sono generalmente molto più veloci dei bundle JavaScript per il caricamento, l'analisi e l'esecuzione dei browser. JavaScript viene digitato dinamicamente e sottoposto a Garbage Collection, il che può causare rallentamenti in fase di esecuzione.
Contro WebGL : WebGL è più veloce di WASM per la maggior parte dei modelli, ma per i modelli più piccoli WASM può superare WebGL a causa dei costi generali fissi di esecuzione degli shader WebGL. La sezione "Quando dovrei usare WASM" di seguito discute le euristiche per prendere questa decisione.
Portabilità e stabilità
WASM dispone di un'aritmetica mobile a 32 bit portatile, che offre parità di precisione su tutti i dispositivi. WebGL, d'altro canto, è specifico dell'hardware e diversi dispositivi possono avere precisione diversa (ad esempio fallback su float a 16 bit su dispositivi iOS).
Come WebGL, WASM è ufficialmente supportato da tutti i principali browser. A differenza di WebGL, WASM può essere eseguito in Node.js ed essere utilizzato lato server senza la necessità di compilare librerie native.
Quando dovrei usare WASM?
Dimensioni del modello e domanda computazionale
In generale, WASM è una buona scelta quando i modelli sono più piccoli o ti interessano i dispositivi di fascia bassa privi di supporto WebGL (estensione OES_texture_float
) o dotati di GPU meno potenti. Il grafico seguente mostra i tempi di inferenza (a partire da TensorFlow.js 1.5.2) in Chrome su un MacBook Pro 2018 per 5 dei nostri modelli ufficialmente supportati sui backend WebGL, WASM e CPU:
Modelli più piccoli
Modello | WebGL | WASM | processore | Memoria |
---|---|---|---|---|
BlazeFace | 22,5 ms | 15,6 ms | 315,2 ms | 0,4 MB |
FaceMesh | 19,3 ms | 19,2 ms | 335 ms | 2,8 MB |
Modelli più grandi
Modello | WebGL | WASM | processore | Memoria |
---|---|---|---|---|
PoseNet | 42,5 ms | 173,9 ms | 1514,7 ms | 4,5MB |
BodyPix | 77 ms | 188,4 ms | 2683 ms | 4,6 MB |
MobileNet v2 | 37 ms | 94 ms | 923,6 ms | 13MB |
La tabella sopra mostra che WASM è 10-30 volte più veloce del semplice backend CPU JS su tutti i modelli e competitivo con WebGL per modelli più piccoli come BlazeFace , che è leggero (400 KB), ma ha un discreto numero di operazioni (~ 140). Dato che i programmi WebGL hanno un costo fisso per esecuzione operativa, questo spiega perché modelli come BlazeFace sono più veloci su WASM.
Questi risultati varieranno a seconda del dispositivo. Il modo migliore per determinare se WASM è adatto alla tua applicazione è testarlo sui nostri diversi backend.
Inferenza vs Formazione
Per affrontare il caso d'uso principale per l'implementazione di modelli pre-addestrati, lo sviluppo del backend WASM darà priorità all'inferenza rispetto al supporto alla formazione . Visualizza un elenco aggiornato delle operazioni supportate in WASM e facci sapere se il tuo modello ha un'operazione non supportata. Per i modelli di training, consigliamo di utilizzare il backend Node (TensorFlow C++) o il backend WebGL.
Back-end della CPU
Il backend della CPU, "cpu", è il backend meno performante, tuttavia è il più semplice. Le operazioni sono tutte implementate in JavaScript vanilla, il che le rende meno parallelizzabili. Bloccano anche il thread dell'interfaccia utente.
Questo backend può essere molto utile per i test o su dispositivi in cui WebGL non è disponibile.
Bandiere
TensorFlow.js dispone di una serie di flag di ambiente che vengono valutati automaticamente e determinano la migliore configurazione nella piattaforma corrente. Questi flag sono per lo più interni, ma alcuni flag globali possono essere controllati con l'API pubblica.
-
tf.enableProdMode():
abilita la modalità di produzione, che rimuoverà la convalida del modello, i controlli NaN e altri controlli di correttezza a favore delle prestazioni. -
tf.enableDebugMode()
: abilita la modalità debug, che registrerà sulla console ogni operazione eseguita, nonché informazioni sulle prestazioni di runtime come l'impronta della memoria e il tempo totale di esecuzione del kernel. Tieni presente che ciò rallenterà notevolmente la tua applicazione, non utilizzarlo in produzione.