API de camadas TensorFlow.js para usuários Keras

A API Layers do TensorFlow.js é modelada a partir de Keras e nos esforçamos para tornar a API Layers o mais semelhante possível ao Keras, dadas as diferenças entre JavaScript e Python. Isso torna mais fácil para usuários com experiência no desenvolvimento de modelos Keras em Python migrarem para camadas TensorFlow.js em JavaScript. Por exemplo, o seguinte código Keras é traduzido em 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();

No entanto, existem algumas diferenças que gostaríamos de destacar e explicar neste documento. Depois de compreender essas diferenças e a lógica por trás delas, sua migração de Python para JavaScript (ou migração na direção inversa) deverá ser uma experiência relativamente tranquila.

Construtores usam objetos JavaScript como configurações

Compare as seguintes linhas Python e JavaScript do exemplo acima: ambas criam uma camada Densa .

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

As funções JavaScript não possuem um equivalente aos argumentos de palavra-chave nas funções Python. Queremos evitar a implementação de opções de construtor como argumentos posicionais em JavaScript, o que seria especialmente complicado de lembrar e usar para construtores com um grande número de argumentos de palavras-chave (por exemplo, LSTM ). É por isso que usamos objetos de configuração JavaScript. Esses objetos fornecem o mesmo nível de invariância posicional e flexibilidade que os argumentos de palavras-chave do Python.

Alguns métodos da classe Model, por exemplo, Model.compile() , também usam um objeto de configuração JavaScript como entrada. No entanto, tenha em mente que Model.fit() , Model.evaluate() e Model.predict() são ligeiramente diferentes. Uma vez que esses métodos usam dados obrigatórios x (recursos) e y (rótulos ou alvos) como entradas; x e y são argumentos posicionais separados do objeto de configuração subsequente que desempenha a função dos argumentos de palavra-chave. Por exemplo:

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

Model.fit() é assíncrono

Model.fit() é o método principal com o qual os usuários realizam o treinamento de modelo no TensorFlow.js. Esse método geralmente pode ser demorado, durando segundos ou minutos. Portanto, utilizamos o recurso async da linguagem JavaScript, para que esta função possa ser usada de uma forma que não bloqueie o thread principal da UI durante a execução no navegador. Isso é semelhante a outras funções potencialmente de longa duração em JavaScript, como async fetch . Observe que async é uma construção que não existe em Python. Enquanto o método fit() em Keras retorna um objeto History, a contrapartida do método fit() em JavaScript retorna uma Promise of History, que pode ser aguardada (como no exemplo acima) ou usada com o método then().

Sem NumPy para TensorFlow.js

Os usuários do Python Keras costumam usar NumPy para realizar operações numéricas e de array básicas, como a geração de tensores 2D no exemplo acima.

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

No TensorFlow.js, esse tipo de operação numérica básica é feita com o próprio pacote. Por exemplo:

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

O namespace tf.* também fornece uma série de outras funções para operações de array e álgebra linear, como multiplicação de matrizes. Consulte a documentação principal do TensorFlow.js para obter mais informações.

Use métodos de fábrica, não construtores

Esta linha em Python (do exemplo acima) é uma chamada de construtor:

# Python:
model = keras.Sequential()

Se traduzida estritamente para JavaScript, a chamada do construtor equivalente seria semelhante a esta:

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

No entanto, decidimos não usar os construtores “novos” porque 1) a palavra-chave “novo” tornaria o código mais inchado e 2) o construtor “novo” é considerado uma “parte ruim” do JavaScript: uma armadilha potencial, como é argumentado em JavaScript: the Good Parts . Para criar modelos e camadas no TensorFlow.js, você chama métodos de fábrica, que possuem nomes lowerCamelCase, por exemplo:

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

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

Os valores da string de opção são lowerCamelCase, não Snake_case

Em JavaScript, é mais comum usar maiúsculas de camelo para nomes de símbolos (por exemplo, consulte o Guia de estilo JavaScript do Google ), em comparação com Python, onde maiúsculas de cobra são comuns (por exemplo, em Keras). Como tal, decidimos usar lowerCamelCase para valores de string para opções incluindo o seguinte:

  • DataFormat, por exemplo, channelsFirst em vez de channels_first
  • Inicializador, por exemplo, glorotNormal em vez de glorot_normal
  • Perda e métricas, por exemplo, meanSquaredError em vez de mean_squared_error , categoricalCrossentropy em vez de categorical_crossentropy .

Por exemplo, como no exemplo acima:

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

Com relação à serialização e desserialização do modelo, fique tranquilo. O mecanismo interno do TensorFlow.js garante que os casos de cobra em objetos JSON sejam tratados corretamente, por exemplo, ao carregar modelos pré-treinados do Python Keras.

Execute objetos Layer com apply(), não chamando-os como funções

No Keras, um objeto Layer possui o método __call__ definido. Portanto, o usuário pode invocar a lógica da camada chamando o objeto como uma função, por exemplo,

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

print(flatten(my_input).shape)

Este açúcar de sintaxe Python é implementado como o método apply() em TensorFlow.js:

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

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

Layer.apply() suporta avaliação imperativa (ansiosa) em tensores concretos

Atualmente, em Keras, o método de chamada só pode operar em objetos tf.Tensor (Python) do TensorFlow (assumindo o back-end do TensorFlow), que são simbólicos e não contêm valores numéricos reais. Isto é o que é mostrado no exemplo da seção anterior. No entanto, no TensorFlow.js, o método apply() de camadas pode operar nos modos simbólico e imperativo. Se apply() for invocado com um SymbolicTensor (uma analogia próxima de tf.Tensor), o valor de retorno será um SymbolicTensor. Isso acontece normalmente durante a construção do modelo. Mas se apply() for invocado com um valor concreto real do Tensor, ele retornará um Tensor concreto. Por exemplo:

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

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

Esse recurso é uma reminiscência do Eager Execution do (Python) TensorFlow. Proporciona maior interatividade e depurabilidade durante o desenvolvimento do modelo, além de abrir portas para a composição de redes neurais dinâmicas.

Os otimizadores estão em treinamento. , não otimizadores.

No Keras, os construtores dos objetos Optimizer estão no namespace keras.optimizers.* . Nas camadas TensorFlow.js, os métodos de fábrica para otimizadores estão no namespace tf.train.* . Por exemplo:

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

loadLayersModel() carrega de um URL, não de um arquivo HDF5

No Keras, os modelos geralmente são salvos como um arquivo HDF5 (.h5), que pode ser carregado posteriormente usando o método keras.models.load_model() . O método segue um caminho para o arquivo .h5. A contrapartida de load_model() em TensorFlow.js é tf.loadLayersModel() . Como HDF5 não é um formato de arquivo amigável ao navegador, tf.loadLayersModel() usa um formato específico do TensorFlow.js. tf.loadLayersModel() usa um arquivo model.json como argumento de entrada. O model.json pode ser convertido de um arquivo Keras HDF5 usando o pacote tensorflowjs pip.

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

Observe também que tf.loadLayersModel() retorna uma Promise de tf.Model .

Em geral, salvar e carregar tf.Model s no TensorFlow.js é feito usando os métodos tf.Model.save e tf.loadLayersModel , respectivamente. Projetamos essas APIs para serem semelhantes às APIs save e load_model do Keras. Mas o ambiente do navegador é bem diferente do ambiente de back-end no qual são executadas estruturas básicas de aprendizado profundo como o Keras, especialmente na variedade de rotas para persistência e transmissão de dados. Portanto, existem algumas diferenças interessantes entre as APIs de salvar/carregar no TensorFlow.js e no Keras. Veja nosso tutorial sobre Salvando e Carregando tf.Model para mais detalhes.

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

No tf.keras do Python TensorFlow, um modelo pode ser treinado usando um objeto Dataset . O método fit() do modelo aceita tal objeto diretamente. Um modelo TensorFlow.js também pode ser treinado com o equivalente JavaScript dos objetos Dataset (consulte a documentação da API tf.data em TensorFlow.js ). No entanto, ao contrário do Python, o treinamento baseado em Dataset é feito por meio de um método dedicado, nomeadamente fitDataset . O método fit() é apenas para treinamento de modelo baseado em tensor.

Gerenciamento de memória de objetos Layer e Model

TensorFlow.js é executado em WebGL no navegador, onde os pesos dos objetos Layer e Model são apoiados por texturas WebGL. No entanto, o WebGL não possui suporte integrado para coleta de lixo. Os objetos Layer e Model gerenciam internamente a memória tensor para o usuário durante suas chamadas de inferência e treinamento. Mas também permitem ao usuário descartá-los para liberar a memória WebGL que ocupam. Isso é útil nos casos em que muitas instâncias de modelo são criadas e liberadas em um único carregamento de página. Para descartar um objeto Layer ou Model, use o método dispose() .