Modelli formativi

Questa guida presuppone che tu abbia già letto la guida ai modelli e ai livelli .

In TensorFlow.js ci sono due modi per addestrare un modello di machine learning:

  1. utilizzando l'API Layers con LayersModel.fit() o LayersModel.fitDataset() .
  2. utilizzando l'API Core con Optimizer.minimize() .

Per prima cosa esamineremo l'API Layers, che è un'API di livello superiore per la creazione e il training di modelli. Quindi, mostreremo come addestrare lo stesso modello utilizzando l'API Core.

introduzione

Un modello di machine learning è una funzione con parametri apprendibili che mappa un input su un output desiderato. I parametri ottimali si ottengono addestrando il modello sui dati.

La formazione prevede diverse fasi:

  • Ottenere un batch di dati nel modello.
  • Chiedere al modello di fare una previsione.
  • Confrontando tale previsione con il valore "vero".
  • Decidere quanto modificare ciascun parametro in modo che il modello possa fare una previsione migliore in futuro per quel batch.

Un modello ben addestrato fornirà una mappatura accurata dall'input all'output desiderato.

Parametri del modello

Definiamo un semplice modello a 2 livelli utilizzando l'API Layers:

const model = tf.sequential({
 layers: [
   tf.layers.dense({inputShape: [784], units: 32, activation: 'relu'}),
   tf.layers.dense({units: 10, activation: 'softmax'}),
 ]
});

Sotto il cofano, i modelli hanno parametri (spesso indicati come pesi ) che possono essere appresi mediante l'addestramento sui dati. Stampiamo i nomi dei pesi associati a questo modello e le loro forme:

model.weights.forEach(w => {
 console.log(w.name, w.shape);
});

Otteniamo il seguente output:

> dense_Dense1/kernel [784, 32]
> dense_Dense1/bias [32]
> dense_Dense2/kernel [32, 10]
> dense_Dense2/bias [10]

Ci sono 4 pesi in totale, 2 per strato denso. Ciò è previsto poiché gli strati densi rappresentano una funzione che mappa il tensore di input x su un tensore di output y tramite l'equazione y = Ax + b dove A (il kernel) e b (il bias) sono parametri dello strato denso.

NOTA: per impostazione predefinita, i livelli densi includono una distorsione, ma è possibile escluderla specificando {useBias: false} nelle opzioni durante la creazione di un livello denso.

model.summary() è un metodo utile se vuoi avere una panoramica del tuo modello e vedere il numero totale di parametri:

Livello (tipo) Forma di uscita Parametro n.
denso_Dense1 (denso) [nullo,32] 25120
denso_Dense2 (denso) [nullo,10] 330
Parametri totali: 25450
Parametri addestrabili: 25450
Parametri non addestrabili: 0

Ogni peso nel modello è backend da un oggetto Variable . In TensorFlow.js, una Variable è un Tensor a virgola mobile con un metodo assign() utilizzato per aggiornare i suoi valori. L'API Layers inizializza automaticamente i pesi utilizzando le migliori pratiche. A scopo dimostrativo, potremmo sovrascrivere i pesi chiamandoassign assign() sulle variabili sottostanti:

model.weights.forEach(w => {
  const newVals = tf.randomNormal(w.shape);
  // w.val is an instance of tf.Variable
  w.val.assign(newVals);
});

Ottimizzatore, perdita e metrica

Prima di fare qualsiasi formazione, devi decidere tre cose:

  1. Un ottimizzatore . Il compito dell'ottimizzatore è decidere quanto modificare ciascun parametro nel modello, data la previsione del modello corrente. Quando utilizzi l'API Layers, puoi fornire un identificatore di stringa di un ottimizzatore esistente (come 'sgd' o 'adam' ) o un'istanza della classe Optimizer .
  2. Una funzione di perdita . Un obiettivo che il modello cercherà di minimizzare. Il suo obiettivo è fornire un singolo numero per "quanto fosse sbagliata" la previsione del modello. La perdita viene calcolata su ogni batch di dati in modo che il modello possa aggiornare i suoi pesi. Quando utilizzi l'API Layers, puoi fornire un identificatore di stringa di una funzione di perdita esistente (come 'categoricalCrossentropy' ) o qualsiasi funzione che accetta un valore previsto e un valore vero e restituisce una perdita. Consulta l' elenco delle perdite disponibili nella nostra documentazione API.
  3. Elenco delle metriche. Analogamente alle perdite, le metriche calcolano un singolo numero, riassumendo il rendimento del nostro modello. Le metriche vengono solitamente calcolate sull'insieme dei dati alla fine di ogni epoca. Per lo meno, vogliamo monitorare che la nostra perdita diminuisca nel tempo. Tuttavia, spesso desideriamo una metrica più a misura d’uomo come l’accuratezza. Quando utilizzi l'API Layers, puoi fornire un identificatore di stringa di una metrica esistente (come 'accuracy' ) o qualsiasi funzione che accetta un valore previsto e un valore vero e restituisce un punteggio. Consulta un elenco delle metriche disponibili nella nostra documentazione API.

Quando hai deciso, compila un LayersModel chiamando model.compile() con le opzioni fornite:

model.compile({
  optimizer: 'sgd',
  loss: 'categoricalCrossentropy',
  metrics: ['accuracy']
});

Durante la compilazione, il modello eseguirà alcune verifiche per assicurarsi che le opzioni scelte siano compatibili tra loro.

Formazione

Esistono due modi per addestrare un LayersModel :

  • Utilizzando model.fit() e fornendo i dati come un grande tensore.
  • Utilizzando model.fitDataset() e fornendo i dati tramite un oggetto Dataset .

modello.fit()

Se il tuo set di dati rientra nella memoria principale ed è disponibile come singolo tensore, puoi addestrare un modello chiamando il metodo fit() :

// Generate dummy data.
const data = tf.randomNormal([100, 784]);
const labels = tf.randomUniform([100, 10]);

function onBatchEnd(batch, logs) {
  console.log('Accuracy', logs.acc);
}

// Train for 5 epochs with batch size of 32.
model.fit(data, labels, {
   epochs: 5,
   batchSize: 32,
   callbacks: {onBatchEnd}
 }).then(info => {
   console.log('Final accuracy', info.history.acc);
 });

Dietro le quinte, model.fit() può fare molto per noi:

  • Suddivide i dati in un set di training e di convalida e utilizza il set di convalida per misurare i progressi durante il training.
  • Mescola i dati ma solo dopo la divisione. Per sicurezza, dovresti pre-mescolare i dati prima di passarli a fit() .
  • Suddivide il tensore di dati di grandi dimensioni in tensori più piccoli di dimensione batchSize.
  • Chiama optimizer.minimize() mentre calcola la perdita del modello rispetto al batch di dati.
  • Può avvisarti dell'inizio e della fine di ogni epoca o batch. Nel nostro caso, riceviamo una notifica alla fine di ogni batch utilizzando l'opzione callbacks.onBatchEnd . Altre opzioni includono: onTrainBegin , onTrainEnd , onEpochBegin , onEpochEnd e onBatchBegin .
  • Si arrende al thread principale per garantire che le attività in coda nel ciclo di eventi JS possano essere gestite in modo tempestivo.

Per ulteriori informazioni, consultare la documentazione di fit() . Tieni presente che se scegli di utilizzare l'API Core, dovrai implementare tu stesso questa logica.

modello.fitDataset()

Se i tuoi dati non rientrano completamente nella memoria o vengono trasmessi in streaming, puoi addestrare un modello chiamando fitDataset() , che accetta un oggetto Dataset . Ecco lo stesso codice di training ma con un set di dati che racchiude una funzione di generatore:

function* data() {
 for (let i = 0; i < 100; i++) {
   // Generate one sample at a time.
   yield tf.randomNormal([784]);
 }
}

function* labels() {
 for (let i = 0; i < 100; i++) {
   // Generate one sample at a time.
   yield tf.randomUniform([10]);
 }
}

const xs = tf.data.generator(data);
const ys = tf.data.generator(labels);
// We zip the data and labels together, shuffle and batch 32 samples at a time.
const ds = tf.data.zip({xs, ys}).shuffle(100 /* bufferSize */).batch(32);

// Train the model for 5 epochs.
model.fitDataset(ds, {epochs: 5}).then(info => {
 console.log('Accuracy', info.history.acc);
});

Per ulteriori informazioni sui set di dati, vedere la documentazione di model.fitDataset() .

Previsione di nuovi dati

Una volta addestrato il modello, puoi chiamare model.predict() per fare previsioni su dati invisibili:

// Predict 3 random samples.
const prediction = model.predict(tf.randomNormal([3, 784]));
prediction.print();

API principale

In precedenza, abbiamo menzionato che esistono due modi per addestrare un modello di machine learning in TensorFlow.js.

La regola generale è provare a utilizzare prima l'API Layers, poiché è modellata sull'API Keras ben adottata. L'API Layers offre anche varie soluzioni standard come l'inizializzazione del peso, la serializzazione dei modelli, la formazione sul monitoraggio, la portabilità e il controllo della sicurezza.

Potresti voler utilizzare l'API Core ogni volta che:

  • Hai bisogno della massima flessibilità o controllo.
  • E non hai bisogno della serializzazione o puoi implementare la tua logica di serializzazione.

Per ulteriori informazioni su questa API, leggere la sezione "Core API" nella guida Modelli e livelli .

Lo stesso modello sopra scritto utilizzando l'API Core si presenta così:

// The weights and biases for the two dense layers.
const w1 = tf.variable(tf.randomNormal([784, 32]));
const b1 = tf.variable(tf.randomNormal([32]));
const w2 = tf.variable(tf.randomNormal([32, 10]));
const b2 = tf.variable(tf.randomNormal([10]));

function model(x) {
  return x.matMul(w1).add(b1).relu().matMul(w2).add(b2);
}

Oltre all'API Layers, anche l'API Data funziona perfettamente con l'API Core. Riutilizziamo il set di dati che abbiamo definito in precedenza nella sezione model.fitDataset() , che esegue lo shuffle e il batch per noi:

const xs = tf.data.generator(data);
const ys = tf.data.generator(labels);
// Zip the data and labels together, shuffle and batch 32 samples at a time.
const ds = tf.data.zip({xs, ys}).shuffle(100 /* bufferSize */).batch(32);

Addestriamo il modello:

const optimizer = tf.train.sgd(0.1 /* learningRate */);
// Train for 5 epochs.
for (let epoch = 0; epoch < 5; epoch++) {
  await ds.forEachAsync(({xs, ys}) => {
    optimizer.minimize(() => {
      const predYs = model(xs);
      const loss = tf.losses.softmaxCrossEntropy(ys, predYs);
      loss.data().then(l => console.log('Loss', l));
      return loss;
    });
  });
  console.log('Epoch', epoch);
}

Il codice riportato sopra è la ricetta standard per l'addestramento di un modello con l'API Core:

  • Ciclo sul numero di epoche.
  • All'interno di ogni epoca, esegui il loop dei tuoi batch di dati. Quando si utilizza un Dataset , dataset.forEachAsync() è un modo conveniente per eseguire il loop sui batch.
  • Per ogni batch, chiama optimizer.minimize(f) , che esegue f e minimizza il suo output calcolando i gradienti rispetto alle quattro variabili che abbiamo definito in precedenza.
  • f calcola la perdita. Chiama una delle funzioni di perdita predefinite utilizzando la previsione del modello e il valore reale.