API des couches TensorFlow.js pour les utilisateurs de Keras

L'API Layers de TensorFlow.js est calquée sur Keras et nous nous efforçons de rendre l' API Layers aussi similaire à Keras que raisonnablement compte tenu des différences entre JavaScript et Python. Cela permet aux utilisateurs expérimentés dans le développement de modèles Keras en Python de migrer plus facilement vers les couches TensorFlow.js en JavaScript. Par exemple, le code Keras suivant se traduit en JavaScript :

# Python:
import keras
import numpy as np

# Build and compile model.
model = keras.Sequential()
model.add(keras.layers.Dense(units=1, input_shape=[1]))
model.compile(optimizer='sgd', loss='mean_squared_error')

# Generate some synthetic data for training.
xs = np.array([[1], [2], [3], [4]])
ys = np.array([[1], [3], [5], [7]])

# Train model with fit().
model.fit(xs, ys, epochs=1000)

# Run inference with predict().
print(model.predict(np.array([[5]])))
// JavaScript:
import * as tf from '@tensorflow/tfjs';

// Build and compile model.
const model = tf.sequential();
model.add(tf.layers.dense({units: 1, inputShape: [1]}));
model.compile({optimizer: 'sgd', loss: 'meanSquaredError'});

// Generate some synthetic data for training.
const xs = tf.tensor2d([[1], [2], [3], [4]], [4, 1]);
const ys = tf.tensor2d([[1], [3], [5], [7]], [4, 1]);

// Train model with fit().
await model.fit(xs, ys, {epochs: 1000});

// Run inference with predict().
model.predict(tf.tensor2d([[5]], [1, 1])).print();

Cependant, il existe certaines différences que nous aimerions souligner et expliquer dans ce document. Une fois que vous avez compris ces différences et leur justification, votre migration de Python vers JavaScript (ou migration dans le sens inverse) devrait être une expérience relativement fluide.

Les constructeurs prennent les objets JavaScript comme configurations

Comparez les lignes Python et JavaScript suivantes de l'exemple ci-dessus : elles créent toutes deux une couche dense .

# Python:
keras.layers.Dense(units=1, inputShape=[1])
// JavaScript:
tf.layers.dense({units: 1, inputShape: [1]});

Les fonctions JavaScript n'ont pas d'équivalent aux arguments de mots clés dans les fonctions Python. Nous voulons éviter d'implémenter des options de constructeur comme arguments de position en JavaScript, ce qui serait particulièrement fastidieux à mémoriser et à utiliser pour les constructeurs avec un grand nombre d'arguments de mots-clés (par exemple, LSTM ). C'est pourquoi nous utilisons des objets de configuration JavaScript. De tels objets offrent le même niveau d'invariance de position et de flexibilité que les arguments de mots-clés Python.

Certaines méthodes de la classe Model, par exemple Model.compile() , prennent également un objet de configuration JavaScript comme entrée. Cependant, gardez à l'esprit que Model.fit() , Model.evaluate() et Model.predict() sont légèrement différents. Étant donné que ces méthodes prennent les données obligatoires x (caractéristiques) et y (étiquettes ou cibles) comme entrées ; x et y sont des arguments de position distincts de l'objet de configuration suivant qui joue le rôle d'arguments de mot-clé. Par exemple:

// JavaScript:
await model.fit(xs, ys, {epochs: 1000});

Model.fit() est asynchrone

Model.fit() est la principale méthode avec laquelle les utilisateurs effectuent la formation de modèles dans TensorFlow.js. Cette méthode peut souvent être de longue durée, durant quelques secondes ou minutes. Par conséquent, nous utilisons la fonctionnalité async du langage JavaScript, afin que cette fonction puisse être utilisée de manière à ne pas bloquer le thread principal de l'interface utilisateur lors de son exécution dans le navigateur. Ceci est similaire à d'autres fonctions potentiellement longues en JavaScript, telles que l' async fetch . Notez async est une construction qui n'existe pas en Python. Alors que la méthode fit() de Keras renvoie un objet History, l'équivalent de la méthode fit() en JavaScript renvoie une promesse d'histoire, qui peut être attendue (comme dans l'exemple ci-dessus) ou utilisée avec la méthode then().

Pas de NumPy pour TensorFlow.js

Les utilisateurs de Python Keras utilisent souvent NumPy pour effectuer des opérations numériques et matricielles de base, telles que la génération de tenseurs 2D dans l'exemple ci-dessus.

# Python:
xs = np.array([[1], [2], [3], [4]])

Dans TensorFlow.js, ce type d'opérations numériques de base est effectué avec le package lui-même. Par exemple:

// JavaScript:
const xs = tf.tensor2d([[1], [2], [3], [4]], [4, 1]);

L'espace de noms tf.* fournit également un certain nombre d'autres fonctions pour les opérations sur les tableaux et l'algèbre linéaire telles que la multiplication matricielle. Consultez la documentation TensorFlow.js Core pour plus d'informations.

Utilisez des méthodes d'usine, pas des constructeurs

Cette ligne en Python (extraite de l'exemple ci-dessus) est un appel de constructeur :

# Python:
model = keras.Sequential()

S'il est traduit strictement en JavaScript, l'appel du constructeur équivalent ressemblerait à ce qui suit :

// JavaScript:
const model = new tf.Sequential();  // !!! DON'T DO THIS !!!

Cependant, nous avons décidé de ne pas utiliser les « nouveaux » constructeurs car 1) le mot-clé « new » rendrait le code plus volumineux et 2) le « nouveau » constructeur est considéré comme une « mauvaise partie » de JavaScript : un piège potentiel, car est argumenté en JavaScript : les bonnes pièces . Pour créer des modèles et des couches dans TensorFlow.js, vous appelez des méthodes d'usine, qui portent des noms LowerCamelCase, par exemple :

// JavaScript:
const model = tf.sequential();

const layer = tf.layers.batchNormalization({axis: 1});

Les valeurs de la chaîne d'option sont lowerCamelCase, pas Snake_case

En JavaScript, il est plus courant d'utiliser la casse chameau pour les noms de symboles (par exemple, voir Google JavaScript Style Guide ), par rapport à Python, où la casse serpent est courante (par exemple, dans Keras). En tant que tel, nous avons décidé d'utiliser lowerCamelCase pour les valeurs de chaîne pour les options comprenant les suivantes :

  • DataFormat, par exemple, channelsFirst au lieu de channels_first
  • Initialiseur, par exemple, glorotNormal au lieu de glorot_normal
  • Perte et métriques, par exemple, meanSquaredError au lieu de mean_squared_error , categoricalCrossentropy au lieu de categorical_crossentropy .

Par exemple, comme dans l'exemple ci-dessus :

// JavaScript:
model.compile({optimizer: 'sgd', loss: 'meanSquaredError'});

En ce qui concerne la sérialisation et la désérialisation des modèles, soyez rassuré. Le mécanisme interne de TensorFlow.js garantit que les cas de serpent dans les objets JSON sont gérés correctement, par exemple lors du chargement de modèles pré-entraînés à partir de Python Keras.

Exécutez les objets Layer avec apply(), pas en les appelant en tant que fonctions

Dans Keras, un objet Layer a la méthode __call__ définie. Par conséquent, l'utilisateur peut invoquer la logique de la couche en appelant l'objet en tant que fonction, par exemple :

# Python:
my_input = keras.Input(shape=[2, 4])
flatten = keras.layers.Flatten()

print(flatten(my_input).shape)

Ce sucre de syntaxe Python est implémenté en tant que méthode apply() dans TensorFlow.js :

// JavaScript:
const myInput = tf.input({shape: [2, 4]});
const flatten = tf.layers.flatten();

console.log(flatten.apply(myInput).shape);

Layer.apply() prend en charge l'évaluation impérative (impatiente) sur les tenseurs concrets

Actuellement, dans Keras, la méthode d'appel ne peut fonctionner que sur les objets tf.Tensor de TensorFlow (Python) (en supposant que le backend TensorFlow), qui sont symboliques et ne contiennent pas de valeurs numériques réelles. C'est ce qui est montré dans l'exemple de la section précédente. Cependant, dans TensorFlow.js, la méthode de couches apply() peut fonctionner à la fois en mode symbolique et impératif. Si apply() est invoqué avec un SymbolicTensor (une analogie proche de tf.Tensor), la valeur de retour sera un SymbolicTensor. Cela se produit généralement lors de la construction d'un modèle. Mais si apply() est invoqué avec une valeur Tensor concrète réelle, il renverra un Tensor concret. Par exemple:

// JavaScript:
const flatten = tf.layers.flatten();

flatten.apply(tf.ones([2, 3, 4])).print();

Cette fonctionnalité rappelle Eager Execution de (Python) TensorFlow. Il offre une plus grande interactivité et une plus grande capacité de débogage pendant le développement du modèle, en plus d'ouvrir les portes à la composition de réseaux neuronaux dynamiques.

Les optimiseurs sont en cours de formation. , pas les optimiseurs.

Dans Keras, les constructeurs des objets Optimizer se trouvent sous l'espace de noms keras.optimizers.* . Dans TensorFlow.js Layers, les méthodes d'usine pour les optimiseurs se trouvent sous l'espace de noms tf.train.* . Par exemple:

# Python:
my_sgd = keras.optimizers.sgd(lr=0.2)
// JavaScript:
const mySGD = tf.train.sgd({lr: 0.2});

loadLayersModel() se charge à partir d'une URL, pas d'un fichier HDF5

Dans Keras, les modèles sont généralement enregistrés sous forme de fichier HDF5 (.h5), qui peut être chargé ultérieurement à l'aide de la méthode keras.models.load_model() . La méthode prend un chemin vers le fichier .h5. La contrepartie de load_model() dans TensorFlow.js est tf.loadLayersModel() . Étant donné que HDF5 n'est pas un format de fichier convivial pour les navigateurs, tf.loadLayersModel() prend un format spécifique à TensorFlow.js. tf.loadLayersModel() prend un fichier model.json comme argument d'entrée. Le model.json peut être converti à partir d'un fichier Keras HDF5 à l'aide du package pip tensorflowjs.

// JavaScript:
const model = await tf.loadLayersModel('https://foo.bar/model.json');

Notez également que tf.loadLayersModel() renvoie une Promise de tf.Model .

En général, l'enregistrement et le chargement tf.Model s dans TensorFlow.js s'effectuent respectivement à l'aide des méthodes tf.Model.save et tf.loadLayersModel . Nous avons conçu ces API pour qu'elles soient similaires aux API save et load_model de Keras. Mais l’environnement du navigateur est assez différent de l’environnement backend sur lequel s’exécutent les principaux frameworks d’apprentissage profond comme Keras, en particulier en ce qui concerne la gamme de routes permettant de conserver et de transmettre les données. Il existe donc des différences intéressantes entre les API de sauvegarde/chargement dans TensorFlow.js et dans Keras. Consultez notre tutoriel sur la sauvegarde et le chargement de tf.Model pour plus de détails.

Utilisez fitDataset() pour entraîner des modèles à l'aide d'objets tf.data.Dataset

Dans tf.keras de Python TensorFlow, un modèle peut être entraîné à l'aide d'un objet Dataset . La méthode fit() du modèle accepte directement un tel objet. Un modèle TensorFlow.js peut également être entraîné avec l'équivalent JavaScript des objets Dataset (voir la documentation de l'API tf.data dans TensorFlow.js ). Cependant, contrairement à Python, la formation basée sur les jeux de données se fait via une méthode dédiée, à savoir fitDataset . La méthode fit() est uniquement destinée à la formation de modèles basés sur des tenseurs.

Gestion de la mémoire des objets Layer et Model

TensorFlow.js s'exécute sur WebGL dans le navigateur, où les poids des objets Layer et Model sont soutenus par des textures WebGL. Cependant, WebGL ne prend pas en charge le garbage collection intégré. Les objets Layer et Model gèrent en interne la mémoire tensorielle pour l'utilisateur lors de ses appels d'inférence et de formation. Mais ils permettent également à l'utilisateur de s'en débarrasser afin de libérer la mémoire WebGL qu'ils occupent. Ceci est utile dans les cas où de nombreuses instances de modèle sont créées et publiées au cours d'un seul chargement de page. Pour supprimer un objet Layer ou Model, utilisez la méthode dispose() .