API de capas de TensorFlow.js para usuarios de Keras

La API de capas de TensorFlow.js sigue el modelo de Keras y nos esforzamos por hacer que la API de capas sea tan similar a Keras como sea razonable dadas las diferencias entre JavaScript y Python. Esto facilita que los usuarios con experiencia en el desarrollo de modelos Keras en Python migren a TensorFlow.js Layers en JavaScript. Por ejemplo, el siguiente código de Keras se traduce a 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();

Sin embargo, hay algunas diferencias que nos gustaría señalar y explicar en este documento. Una vez que comprenda estas diferencias y la razón detrás de ellas, su migración de Python a JavaScript (o la migración en la dirección inversa) debería ser una experiencia relativamente fluida.

Los constructores toman objetos de JavaScript como configuraciones

Compare las siguientes líneas de Python y JavaScript del ejemplo anterior: ambas crean una capa densa .

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

Las funciones de JavaScript no tienen un equivalente de los argumentos de palabras clave en las funciones de Python. Queremos evitar la implementación de opciones de constructores como argumentos posicionales en JavaScript, lo que sería especialmente engorroso de recordar y usar para constructores con una gran cantidad de argumentos de palabras clave (por ejemplo, LSTM ). Es por eso que usamos objetos de configuración de JavaScript. Dichos objetos proporcionan el mismo nivel de invariancia posicional y flexibilidad que los argumentos de palabras clave de Python.

Algunos métodos de la clase Model, por ejemplo, Model.compile() , también toman un objeto de configuración de JavaScript como entrada. Sin embargo, tenga en cuenta que Model.fit() , Model.evaluate() y Model.predict() son ligeramente diferentes. Dado que estos métodos toman datos obligatorios x (características) e y (etiquetas u objetivos) como entradas; x e y son argumentos posicionales separados del objeto de configuración resultante que desempeña el papel de los argumentos de palabras clave. Por ejemplo:

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

Model.fit() es asíncrono

Model.fit() es el método principal con el que los usuarios realizan el entrenamiento de modelos en TensorFlow.js. Este método a menudo puede ser de larga duración, con una duración de segundos o minutos. Por lo tanto, utilizamos la función async del lenguaje JavaScript, para que esta función se pueda usar de una manera que no bloquee el hilo principal de la interfaz de usuario cuando se ejecuta en el navegador. Esto es similar a otras funciones potencialmente de larga duración en JavaScript, como la búsqueda async . Tenga en cuenta que async es una construcción que no existe en Python. Mientras que el método fit() en Keras devuelve un objeto Historial, la contraparte del método fit() en JavaScript devuelve una Promesa de Historial, que se puede esperar (como en el ejemplo anterior) o usarse con el método entonces().

Sin NumPy para TensorFlow.js

Los usuarios de Python Keras a menudo usan NumPy para realizar operaciones numéricas y de matriz básicas, como generar tensores 2D en el ejemplo anterior.

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

En TensorFlow.js, este tipo de operaciones numéricas básicas se realizan con el propio paquete. Por ejemplo:

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

El espacio de nombres tf.* también proporciona otras funciones para operaciones de álgebra lineal y de matrices, como la multiplicación de matrices. Consulte la documentación principal de TensorFlow.js para obtener más información.

Use métodos de fábrica, no constructores

Esta línea en Python (del ejemplo anterior) es una llamada al constructor:

# Python:
model = keras.Sequential()

Si se traduce estrictamente a JavaScript, la llamada al constructor equivalente sería similar a la siguiente:

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

Sin embargo, decidimos no usar los constructores "nuevos" porque 1) la palabra clave "nuevo" haría que el código se hinchara más y 2) el constructor "nuevo" se considera una "parte mala" de JavaScript: una trampa potencial, ya que Se argumenta en JavaScript: las Partes Buenas . Para crear modelos y capas en TensorFlow.js, llame a los métodos de fábrica, que tienen nombres de CamelCase en minúsculas, por ejemplo:

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

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

Los valores de cadena de opciones son lowerCamelCase, no snake_case

En JavaScript, es más común usar mayúsculas y minúsculas para nombres de símbolos (por ejemplo, consulte la Guía de estilo de JavaScript de Google ), en comparación con Python, donde las mayúsculas y minúsculas son comunes (por ejemplo, en Keras). Como tal, decidimos usar lowerCamelCase para valores de cadena para opciones que incluyen lo siguiente:

  • DataFormat, por ejemplo, channelsFirst en lugar de channels_first
  • Inicializador, por ejemplo, glorotNormal en lugar de glorot_normal
  • Pérdida y métricas, por ejemplo, meanSquaredError en lugar de mean_squared_error , categoricalCrossentropy en lugar de categorical_crossentropy .

Por ejemplo, como en el ejemplo anterior:

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

Con respecto a la serialización y deserialización de modelos, puede estar tranquilo. El mecanismo interno de TensorFlow.js garantiza que los casos de serpientes en los objetos JSON se manejen correctamente, por ejemplo, al cargar modelos previamente entrenados desde Python Keras.

Ejecute objetos de capa con apply (), no llamándolos como funciones

En Keras, un objeto Layer tiene definido el método __call__ . Por lo tanto, el usuario puede invocar la lógica de la capa llamando al objeto como una función, por ejemplo,

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

print(flatten(my_input).shape)

Este azúcar de sintaxis de Python se implementa como el método apply() en TensorFlow.js:

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

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

Layer.apply() admite la evaluación imperativa (ansiosa) en tensores concretos

Actualmente, en Keras, el método de llamada solo puede operar en (Python) los objetos tf.Tensor de TensorFlow (suponiendo el backend de TensorFlow), que son simbólicos y no contienen valores numéricos reales. Esto es lo que se muestra en el ejemplo de la sección anterior. Sin embargo, en TensorFlow.js, el método apply() de capas puede funcionar tanto en modo simbólico como imperativo. Si se invoca apply() con un SymbolicTensor (una analogía cercana a tf.Tensor), el valor devuelto será un SymbolicTensor. Esto sucede típicamente durante la construcción del modelo. Pero si se invoca apply() con un valor de Tensor concreto real, devolverá un Tensor concreto. Por ejemplo:

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

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

Esta función recuerda a Eager Execution de (Python) TensorFlow. Permite una mayor interactividad y depuración durante el desarrollo del modelo, además de abrir las puertas a la composición de redes neuronales dinámicas.

Los optimizadores están bajo entrenamiento. , no optimizadores.

En Keras, los constructores de los objetos Optimizer se encuentran en el espacio de nombres keras.optimizers.* . En las capas de TensorFlow.js, los métodos de fábrica para los optimizadores se encuentran en el espacio de nombres tf.train.* . Por ejemplo:

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

loadLayersModel() se carga desde una URL, no desde un archivo HDF5

En Keras, los modelos generalmente se guardan como un archivo HDF5 (.h5), que luego se puede cargar usando el método keras.models.load_model() . El método toma una ruta al archivo .h5. La contraparte de load_model() en TensorFlow.js es tf.loadLayersModel() . Dado que HDF5 no es un formato de archivo compatible con el navegador, tf.loadLayersModel() toma un formato específico de TensorFlow.js. tf.loadLayersModel() toma un archivo model.json como argumento de entrada. El model.json se puede convertir desde un archivo Keras HDF5 usando el paquete pip tensorflowjs.

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

También tenga en cuenta que tf.loadLayersModel() devuelve una Promise de tf.Model .

En general, guardar y cargar tf.Model s en TensorFlow.js se realiza mediante los métodos tf.Model.save y tf.loadLayersModel , respectivamente. Diseñamos estas API para que sean similares a la API save y load_model de Keras. Pero el entorno del navegador es bastante diferente del entorno de back-end en el que se ejecutan los marcos básicos de aprendizaje profundo como Keras, particularmente en la variedad de rutas para persistir y transmitir datos. Por lo tanto, existen algunas diferencias interesantes entre las API de guardar/cargar en TensorFlow.js y en Keras. Consulte nuestro tutorial sobre cómo guardar y cargar tf.Model para obtener más detalles.

Use fitDataset() para entrenar modelos usando objetos tf.data.Dataset

En tf.keras de Python TensorFlow, un modelo se puede entrenar usando un objeto Dataset . El método fit() del modelo acepta dicho objeto directamente. Un modelo de TensorFlow.js también se puede entrenar con el equivalente de JavaScript de los objetos del conjunto de datos (consulte la documentación de la API tf.data en TensorFlow.js ). Sin embargo, a diferencia de Python, el entrenamiento basado en conjuntos de datos se realiza a través de un método dedicado, a saber, fitDataset . El método fit() es solo para el entrenamiento de modelos basados ​​en tensores.

Gestión de memoria de objetos de capa y modelo

TensorFlow.js se ejecuta en WebGL en el navegador, donde los pesos de los objetos Layer y Model están respaldados por texturas WebGL. Sin embargo, WebGL no tiene soporte de recolección de basura integrado. Los objetos Layer y Model administran internamente la memoria de tensor para el usuario durante sus llamadas de inferencia y entrenamiento. Pero también permiten al usuario disponer de ellos para liberar la memoria WebGL que ocupan. Esto es útil en los casos en que se crean y publican muchas instancias de modelo en una sola carga de página. Para desechar un objeto Layer o Model, utilice el método dispose() .