TensorFlow.js fonctionne dans le navigateur et Node.js, et sur les deux plates-formes, il existe de nombreuses configurations différentes disponibles. Chaque plateforme comporte un ensemble unique de considérations qui affecteront la manière dont les applications sont développées.
Dans le navigateur, TensorFlow.js prend en charge les appareils mobiles ainsi que les appareils de bureau. Chaque appareil dispose d'un ensemble spécifique de contraintes, comme les API WebGL disponibles, qui sont automatiquement déterminées et configurées pour vous.
Dans Node.js, TensorFlow.js prend en charge la liaison directe à l'API TensorFlow ou l'exécution avec les implémentations de processeur Vanilla plus lentes.
Environnements
Lorsqu'un programme TensorFlow.js est exécuté, la configuration spécifique est appelée environnement. L'environnement est composé d'un seul backend global ainsi que d'un ensemble d'indicateurs qui contrôlent les fonctionnalités précises de TensorFlow.js.
Backends
TensorFlow.js prend en charge plusieurs backends différents qui implémentent le stockage tensoriel et les opérations mathématiques. À un moment donné, un seul backend est actif. La plupart du temps, TensorFlow.js choisira automatiquement le meilleur backend pour vous en fonction de l'environnement actuel. Cependant, il est parfois important de savoir quel backend est utilisé et comment le changer.
Pour trouver le backend que vous utilisez :
console.log(tf.getBackend());
Si vous souhaitez modifier manuellement le backend :
tf.setBackend('cpu');
console.log(tf.getBackend());
Back-end WebGL
Le backend WebGL, « webgl », est actuellement le backend le plus puissant pour le navigateur. Ce backend est jusqu’à 100 fois plus rapide que le backend CPU vanilla. Les tenseurs sont stockés sous forme de textures WebGL et les opérations mathématiques sont implémentées dans les shaders WebGL. Voici quelques éléments utiles à savoir lors de l'utilisation de ce backend : \
Évitez de bloquer le fil de discussion de l'interface utilisateur
Lorsqu'une opération est appelée, comme tf.matMul(a, b), le tf.Tensor résultant est renvoyé de manière synchrone, mais le calcul de la multiplication matricielle n'est peut-être pas encore prêt. Cela signifie que le tf.Tensor renvoyé n'est qu'un handle pour le calcul. Lorsque vous appelez x.data()
ou x.array()
, les valeurs seront résolues une fois le calcul terminé. Il est donc important d'utiliser les méthodes asynchrones x.data()
et x.array()
par rapport à leurs homologues synchrones x.dataSync()
et x.arraySync()
pour éviter de bloquer le thread de l'interface utilisateur pendant la fin du calcul.
Gestion de la mémoire
Une mise en garde lors de l'utilisation du backend WebGL est la nécessité d'une gestion explicite de la mémoire. Les WebGLTextures, où les données Tensor sont finalement stockées, ne sont pas automatiquement récupérées par le navigateur.
Pour détruire la mémoire d'un tf.Tensor
, vous pouvez utiliser la méthode dispose()
:
const a = tf.tensor([[1, 2], [3, 4]]);
a.dispose();
Il est très courant d’enchaîner plusieurs opérations dans une application. Conserver une référence à toutes les variables intermédiaires pour les supprimer peut réduire la lisibilité du code. Pour résoudre ce problème, TensorFlow.js fournit une méthode tf.tidy()
qui nettoie tous tf.Tensor
qui ne sont pas renvoyés par une fonction après son exécution, de la même manière que les variables locales sont nettoyées lorsqu'une fonction est exécutée :
const a = tf.tensor([[1, 2], [3, 4]]);
const y = tf.tidy(() => {
const result = a.square().log().neg();
return result;
});
Précision
Sur les appareils mobiles, WebGL peut uniquement prendre en charge les textures à virgule flottante 16 bits. Cependant, la plupart des modèles d'apprentissage automatique sont entraînés avec des pondérations et des activations à virgule flottante de 32 bits. Cela peut entraîner des problèmes de précision lors du portage d'un modèle pour un appareil mobile, car les nombres flottants 16 bits ne peuvent représenter que des nombres compris dans la plage [0.000000059605, 65504]
. Cela signifie que vous devez faire attention à ce que les poids et les activations de votre modèle ne dépassent pas cette plage. Pour vérifier si l'appareil prend en charge les textures 32 bits, vérifiez la valeur de tf.ENV.getBool('WEBGL_RENDER_FLOAT32_CAPABLE')
, si cela est faux, l'appareil ne prend en charge que les textures à virgule flottante 16 bits. Vous pouvez utiliser tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED')
pour vérifier si TensorFlow.js utilise actuellement des textures 32 bits.
Compilation de shaders et téléchargements de textures
TensorFlow.js exécute des opérations sur le GPU en exécutant des programmes de shader WebGL. Ces shaders sont assemblés et compilés paresseusement lorsque l'utilisateur demande à exécuter une opération. La compilation d'un shader se fait sur le CPU du thread principal et peut être lente. TensorFlow.js mettra automatiquement en cache les shaders compilés, rendant le deuxième appel à la même opération avec des tenseurs d'entrée et de sortie de même forme beaucoup plus rapide. En règle générale, les applications TensorFlow.js utilisent les mêmes opérations plusieurs fois au cours de la durée de vie de l'application, de sorte que le deuxième passage dans un modèle d'apprentissage automatique est beaucoup plus rapide.
TensorFlow.js stocke également les données tf.Tensor sous forme de WebGLTextures. Lorsqu'un tf.Tensor
est créé, nous ne téléchargeons pas immédiatement les données sur le GPU, mais nous conservons les données sur le CPU jusqu'à ce que le tf.Tensor
soit utilisé dans une opération. Si le tf.Tensor
est utilisé une deuxième fois, les données sont déjà sur le GPU donc il n'y a pas de frais de téléchargement. Dans un modèle d'apprentissage automatique typique, cela signifie que les poids sont téléchargés lors de la première prédiction via le modèle et que le deuxième passage dans le modèle sera beaucoup plus rapide.
Si vous vous souciez des performances de la première prédiction via votre modèle ou votre code TensorFlow.js, nous vous recommandons de réchauffer le modèle en transmettant un Tensor d'entrée de la même forme avant d'utiliser des données réelles.
Par exemple:
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 Node.js
Dans le backend TensorFlow Node.js, « node », l'API TensorFlow C est utilisée pour accélérer les opérations. Cela utilisera l'accélération matérielle disponible de la machine, comme CUDA, si disponible.
Dans ce backend, tout comme le backend WebGL, les opérations renvoient les tf.Tensor
de manière synchrone. Cependant, contrairement au backend WebGL, l'opération est terminée avant que vous récupériez le tenseur. Cela signifie qu'un appel à tf.matMul(a, b)
bloquera le thread de l'interface utilisateur.
Pour cette raison, si vous avez l'intention de l'utiliser dans une application de production, vous devez exécuter TensorFlow.js dans les threads de travail pour ne pas bloquer le thread principal.
Pour plus d'informations sur Node.js, consultez ce guide.
Back-end WASM
TensorFlow.js fournit un backend WebAssembly ( wasm
), qui offre une accélération du processeur et peut être utilisé comme alternative au processeur JavaScript Vanilla ( cpu
) et aux backends accélérés WebGL ( webgl
). Pour l'utiliser :
// Set the backend to WASM and wait for the module to be ready.
tf.setBackend('wasm');
tf.ready().then(() => {...});
Si votre serveur diffuse le fichier .wasm
sur un chemin différent ou sous un nom différent, utilisez setWasmPath
avant d'initialiser le backend. Consultez la section « Utilisation des bundles » dans le fichier README pour plus d'informations :
import {setWasmPath} from '@tensorflow/tfjs-backend-wasm';
setWasmPath(yourCustomPath);
tf.setBackend('wasm');
tf.ready().then(() => {...});
Pourquoi WASM ?
WASM a été introduit en 2015 en tant que nouveau format binaire basé sur le Web, fournissant aux programmes écrits en JavaScript, C, C++, etc. une cible de compilation pour une exécution sur le Web. WASM est pris en charge par Chrome, Safari, Firefox et Edge depuis 2017 et est pris en charge par 90 % des appareils dans le monde.
Performance
Le backend WASM exploite la bibliothèque XNNPACK pour une implémentation optimisée des opérateurs de réseaux neuronaux.
Par rapport à JavaScript : les binaires WASM sont généralement beaucoup plus rapides que les bundles JavaScript pour que les navigateurs se chargent, analysent et s'exécutent. JavaScript est typé dynamiquement et ramassé, ce qui peut entraîner des ralentissements au moment de l'exécution.
Par rapport à WebGL : WebGL est plus rapide que WASM pour la plupart des modèles, mais pour les petits modèles, WASM peut surpasser WebGL en raison des frais généraux fixes liés à l'exécution des shaders WebGL. La section « Quand dois-je utiliser WASM » ci-dessous traite des heuristiques permettant de prendre cette décision.
Portabilité et stabilité
WASM dispose d'une arithmétique flottante portable de 32 bits, offrant une parité de précision sur tous les appareils. WebGL, en revanche, est spécifique au matériel et différents appareils peuvent avoir une précision variable (par exemple, repli sur des flotteurs 16 bits sur les appareils iOS).
Comme WebGL, WASM est officiellement pris en charge par tous les principaux navigateurs. Contrairement à WebGL, WASM peut s'exécuter dans Node.js et être utilisé côté serveur sans avoir besoin de compiler des bibliothèques natives.
Quand dois-je utiliser WASM ?
Taille du modèle et demande de calcul
En général, WASM est un bon choix lorsque les modèles sont plus petits ou que vous vous souciez des appareils bas de gamme qui ne prennent pas en charge WebGL (extension OES_texture_float
) ou disposent de GPU moins puissants. Le graphique ci-dessous montre les temps d'inférence (à partir de TensorFlow.js 1.5.2) dans Chrome sur un MacBook Pro 2018 pour 5 de nos modèles officiellement pris en charge sur les backends WebGL, WASM et CPU :
Modèles plus petits
Modèle | WebGL | WASM | Processeur | Mémoire |
---|---|---|---|---|
BlazeVisage | 22,5 ms | 15,6 ms | 315,2 ms | 0,4 Mo |
FaceMesh | 19,3 ms | 19,2 ms | 335 ms | 2,8 Mo |
Modèles plus grands
Modèle | WebGL | WASM | Processeur | Mémoire |
---|---|---|---|---|
PoseNet | 42,5 ms | 173,9 ms | 1514,7 ms | 4,5 Mo |
CorpsPix | 77 ms | 188,4 ms | 2683 ms | 4,6 Mo |
MobileNet v2 | 37 ms | 94 ms | 923,6 ms | 13 Mo |
Le tableau ci-dessus montre que WASM est 10 à 30 fois plus rapide que le backend CPU JS simple sur tous les modèles, et compétitif avec WebGL pour les modèles plus petits comme BlazeFace , qui est léger (400 Ko), mais dispose d'un nombre décent d'opérations (~ 140). Étant donné que les programmes WebGL ont un coût fixe par exécution d'opération, cela explique pourquoi des modèles comme BlazeFace sont plus rapides sur WASM.
Ces résultats varient en fonction de votre appareil. La meilleure façon de déterminer si WASM convient à votre application est de le tester sur nos différents backends.
Inférence vs formation
Pour répondre au principal cas d'utilisation du déploiement de modèles pré-entraînés, le développement backend WASM donnera la priorité à l'inférence plutôt qu'à la prise en charge de la formation . Consultez une liste à jour des opérations prises en charge dans WASM et faites-nous savoir si votre modèle a une opération non prise en charge. Pour les modèles de formation, nous vous recommandons d'utiliser le backend Node (TensorFlow C++) ou le backend WebGL.
Processeur principal
Le backend CPU, 'cpu', est le backend le moins performant, mais c'est le plus simple. Les opérations sont toutes implémentées en JavaScript Vanilla, ce qui les rend moins parallélisables. Ils bloquent également le fil de discussion de l'interface utilisateur.
Ce backend peut être très utile pour les tests ou sur les appareils sur lesquels WebGL n'est pas disponible.
Drapeaux
TensorFlow.js dispose d'un ensemble d'indicateurs d'environnement qui sont automatiquement évalués et déterminent la meilleure configuration dans la plate-forme actuelle. Ces indicateurs sont pour la plupart internes, mais quelques indicateurs globaux peuvent être contrôlés avec l'API publique.
-
tf.enableProdMode():
active le mode production, qui supprimera la validation du modèle, les contrôles NaN et autres contrôles d'exactitude en faveur des performances. -
tf.enableDebugMode()
: active le mode débogage, qui enregistrera sur la console chaque opération exécutée, ainsi que les informations sur les performances d'exécution telles que l'empreinte mémoire et la durée totale d'exécution du noyau. Notez que cela ralentira considérablement votre application, ne l'utilisez pas en production.