API dei livelli TensorFlow.js per gli utenti Keras

L'API Layers di TensorFlow.js è modellata su Keras e ci sforziamo di rendere l' API Layers tanto simile a Keras quanto ragionevole date le differenze tra JavaScript e Python. Ciò semplifica la migrazione ai livelli TensorFlow.js in JavaScript per gli utenti con esperienza nello sviluppo di modelli Keras in Python. Ad esempio, il seguente codice Keras si traduce in 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();

Tuttavia, ci sono alcune differenze che vorremmo evidenziare e spiegare in questo documento. Una volta comprese queste differenze e la logica alla base di esse, la migrazione da Python a JavaScript (o la migrazione nella direzione opposta) dovrebbe essere un'esperienza relativamente fluida.

I costruttori accettano oggetti JavaScript come configurazioni

Confronta le seguenti righe Python e JavaScript dell'esempio sopra: entrambe creano un livello Dense .

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

Le funzioni JavaScript non hanno un equivalente degli argomenti chiave nelle funzioni Python. Vogliamo evitare di implementare le opzioni del costruttore come argomenti posizionali in JavaScript, che sarebbero particolarmente scomodi da ricordare e utilizzare per costruttori con un gran numero di argomenti di parole chiave (ad esempio, LSTM ). Questo è il motivo per cui utilizziamo oggetti di configurazione JavaScript. Tali oggetti forniscono lo stesso livello di invarianza di posizione e flessibilità degli argomenti delle parole chiave Python.

Alcuni metodi della classe Model, ad esempio Model.compile() , accettano anche un oggetto di configurazione JavaScript come input. Tuttavia, tieni presente che Model.fit() , Model.evaluate() e Model.predict() sono leggermente diversi. Poiché questi metodi accettano dati x (caratteristiche) e y (etichette o obiettivi) obbligatori come input; x y argomenti posizionali separati dal conseguente oggetto di configurazione che svolge il ruolo degli argomenti della parola chiave. Per esempio:

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

Model.fit() è asincrono

Model.fit() è il metodo principale con cui gli utenti eseguono l'addestramento del modello in TensorFlow.js. Questo metodo può spesso essere di lunga durata, durando secondi o minuti. Pertanto, utilizziamo la funzionalità async del linguaggio JavaScript, in modo che questa funzione possa essere utilizzata in modo da non bloccare il thread principale dell'interfaccia utente durante l'esecuzione nel browser. Questo è simile ad altre funzioni potenzialmente di lunga durata in JavaScript, come async fetch . Nota che async è un costrutto che non esiste in Python. Mentre il metodo fit() in Keras restituisce un oggetto History, la controparte del metodo fit() in JavaScript restituisce una Promise of History, che può essere attesa (come nell'esempio sopra) o utilizzata con il metodo then().

Nessun NumPy per TensorFlow.js

Gli utenti di Python Keras utilizzano spesso NumPy per eseguire operazioni numeriche e di array di base, come la generazione di tensori 2D nell'esempio precedente.

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

In TensorFlow.js, questo tipo di operazioni numeriche di base vengono eseguite con il pacchetto stesso. Per esempio:

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

Lo spazio dei nomi tf.* fornisce anche una serie di altre funzioni per operazioni su matrici e algebra lineare come la moltiplicazione di matrici. Consulta la documentazione di TensorFlow.js Core per ulteriori informazioni.

Utilizzare metodi di fabbrica, non costruttori

Questa riga in Python (dall'esempio sopra) è una chiamata del costruttore:

# Python:
model = keras.Sequential()

Se tradotta rigorosamente in JavaScript, la chiamata del costruttore equivalente sarebbe simile alla seguente:

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

Tuttavia, abbiamo deciso di non utilizzare i costruttori “new” perché 1) la parola chiave “new” renderebbe il codice più gonfio e 2) il costruttore “new” è considerato una “parte cattiva” di JavaScript: una potenziale trappola, poiché si sostiene in JavaScript: le parti buone . Per creare modelli e layer in TensorFlow.js, chiami i metodi factory, che hanno nomi LowerCamelCase, ad esempio:

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

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

I valori della stringa delle opzioni sono lowerCamelCase, non snake_case

In JavaScript, è più comune utilizzare il maiuscolo/minuscolo del cammello per i nomi dei simboli (ad esempio, vedere la Guida allo stile di Google JavaScript ), rispetto a Python, dove il maiuscolo/minuscolo del serpente è comune (ad esempio, in Keras). Pertanto, abbiamo deciso di utilizzare lowerCamelCase per i valori stringa per le opzioni tra cui:

  • DataFormat, ad esempio, channelsFirst invece di channels_first
  • Inizializzatore, ad esempio, glorotNormal invece di glorot_normal
  • Perdita e metriche, ad esempio, meanSquaredError invece di mean_squared_error , categoricalCrossentropy invece di categorical_crossentropy .

Ad esempio, come nell'esempio sopra:

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

Per quanto riguarda la serializzazione e deserializzazione del modello, state tranquilli. Il meccanismo interno di TensorFlow.js garantisce che i casi di serpente negli oggetti JSON vengano gestiti correttamente, ad esempio durante il caricamento di modelli pre-addestrati da Python Keras.

Esegui gli oggetti Layer con apply(), non chiamandoli come funzioni

In Keras, un oggetto Layer ha il metodo __call__ definito. Pertanto l'utente può invocare la logica del livello chiamando l'oggetto come una funzione, ad esempio,

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

print(flatten(my_input).shape)

Questo zucchero della sintassi Python è implementato come metodo apply() in TensorFlow.js:

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

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

Layer.apply() supporta la valutazione imperativa (appassionata) su tensori concreti

Attualmente, in Keras, il metodo di chiamata può operare solo sugli oggetti tf.Tensor di TensorFlow (Python) (presupponendo il backend TensorFlow), che sono simbolici e non contengono valori numerici effettivi. Questo è ciò che viene mostrato nell'esempio della sezione precedente. Tuttavia, in TensorFlow.js, il metodo apply() dei livelli può operare sia in modalità simbolica che imperativa. Se apply() viene invocato con un SymbolicTensor (una stretta analogia di tf.Tensor), il valore restituito sarà un SymbolicTensor. Ciò accade in genere durante la creazione del modello. Ma se apply() viene invocato con un valore tensore concreto effettivo, restituirà un tensore concreto. Per esempio:

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

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

Questa funzionalità ricorda Eager Execution di (Python) TensorFlow. Offre maggiore interattività e possibilità di debug durante lo sviluppo del modello, oltre ad aprire le porte alla composizione di reti neurali dinamiche.

Gli ottimizzatori sono sotto tiro. , non ottimizzatori.

In Keras, i costruttori per gli oggetti Optimizer si trovano nello spazio dei nomi keras.optimizers.* . Nei livelli TensorFlow.js, i metodi factory per gli ottimizzatori si trovano nello spazio dei nomi tf.train.* . Per esempio:

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

loadLayersModel() viene caricato da un URL, non da un file HDF5

In Keras, i modelli vengono solitamente salvati come file HDF5 (.h5), che può essere successivamente caricato utilizzando il metodo keras.models.load_model() . Il metodo prende un percorso per il file .h5. La controparte di load_model() in TensorFlow.js è tf.loadLayersModel() . Poiché HDF5 non è un formato di file adatto ai browser, tf.loadLayersModel() accetta un formato specifico di TensorFlow.js. tf.loadLayersModel() accetta un file model.json come argomento di input. Il model.json può essere convertito da un file Keras HDF5 utilizzando il pacchetto pip tensorflowjs.

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

Tieni inoltre presente che tf.loadLayersModel() restituisce una Promise di tf.Model .

In generale, il salvataggio e il caricamento di tf.Model in TensorFlow.js vengono eseguiti utilizzando rispettivamente i metodi tf.Model.save e tf.loadLayersModel . Abbiamo progettato queste API in modo che siano simili alle API save e load_model di Keras. Ma l’ambiente del browser è molto diverso dall’ambiente back-end su cui vengono eseguiti i principali framework di deep learning come Keras, in particolare nella serie di percorsi per la persistenza e la trasmissione dei dati. Quindi ci sono alcune differenze interessanti tra le API di salvataggio/caricamento in TensorFlow.js e in Keras. Consulta il nostro tutorial sul salvataggio e il caricamento di tf.Model per maggiori dettagli.

Utilizza fitDataset() per addestrare i modelli utilizzando oggetti tf.data.Dataset

In tf.keras di Python TensorFlow, un modello può essere addestrato utilizzando un oggetto Dataset . Il metodo fit() del modello accetta direttamente tale oggetto. Un modello TensorFlow.js può essere addestrato anche con l'equivalente JavaScript degli oggetti Dataset (vedere la documentazione dell'API tf.data in TensorFlow.js ). Tuttavia, a differenza di Python, l'addestramento basato su Dataset viene eseguito tramite un metodo dedicato, ovvero fitDataset . Il metodo fit() è solo per l'addestramento del modello basato su tensori.

Gestione della memoria degli oggetti Layer e Model

TensorFlow.js viene eseguito su WebGL nel browser, dove i pesi degli oggetti Layer e Model sono supportati da texture WebGL. Tuttavia, WebGL non dispone del supporto integrato per la raccolta dei rifiuti. Gli oggetti Layer e Model gestiscono internamente la memoria tensore per l'utente durante le chiamate di inferenza e training. Ma permettono anche all'utente di eliminarli per liberare la memoria WebGL che occupano. Ciò è utile nei casi in cui vengono create e rilasciate molte istanze del modello all'interno del caricamento di una singola pagina. Per eliminare un oggetto Layer o Model, utilizzare il metodo dispose() .