Partecipa al simposio Women in ML il 7 dicembre Registrati ora

Modelli di allenamento

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

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 principale con Optimizer.minimize() .

In primo luogo, esamineremo l'API Layers, che è un'API di livello superiore per la creazione e l'addestramento di modelli. Quindi, mostreremo come addestrare lo stesso modello utilizzando l'API Core.

introduzione

Un modello di apprendimento automatico è una funzione con parametri apprendibili che associa un input a un output desiderato. I parametri ottimali si ottengono addestrando il modello sui dati.

La formazione prevede diversi passaggi:

  • Ottenere un batch di dati nel modello.
  • Chiedere al modello di fare una previsione.
  • Confrontando quella previsione con il valore "vero".
  • Decidere quanto modificare ogni 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'allenamento 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) b (il bias) sono parametri dello strato denso.

NOTA: per impostazione predefinita, i livelli densi includono una distorsione, ma puoi 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 Param #
denso_denso1 (denso) [nullo,32] 25120
denso_denso2 (denso) [nullo,10] 330
Totale parametri: 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 aggiuntivo assign() utilizzato per aggiornarne i valori. L'API Layers inizializza automaticamente i pesi utilizzando le migliori pratiche. Per motivi di dimostrazione, potremmo sovrascrivere i pesi chiamando 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 allenamento, devi decidere tre cose:

  1. Un ottimizzatore . Il compito dell'ottimizzatore è decidere quanto modificare ogni parametro nel modello, data la previsione del modello corrente. Quando si utilizza l'API Layers, è possibile 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 unico 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 si utilizza l'API Layers, è possibile 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 un elenco delle perdite disponibili nei nostri documenti API.
  3. Elenco delle metriche. Simile alle perdite, le metriche calcolano un singolo numero, riassumendo quanto bene sta andando il nostro modello. Le metriche vengono solitamente calcolate sull'intero dato alla fine di ogni epoca. Per lo meno, vogliamo monitorare che la nostra perdita stia diminuendo nel tempo. Tuttavia, spesso desideriamo una metrica più umana come la precisione. Quando si utilizza l'API Layers, è possibile fornire un identificatore di stringa di una metrica esistente (ad esempio 'accuracy' ) o qualsiasi funzione che accetta un valore previsto e un valore vero e restituisce un punteggio. Consulta un elenco di metriche disponibili nei nostri documenti API.

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

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

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

Addestramento

Esistono due modi per addestrare un LayersModel :

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

modello.fit()

Se il tuo set di dati si adatta alla memoria principale ed è disponibile come un 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);
 });

Sotto il cofano, model.fit() può fare molto per noi:

  • Suddivide i dati in un treno e un set di convalida e utilizza il set di convalida per misurare i progressi durante l'addestramento.
  • Mescola i dati ma solo dopo la divisione. Per sicurezza, dovresti pre-mischiare i dati prima di passarli a fit() .
  • Suddivide il tensore dei dati di grandi dimensioni in tensori più piccoli di dimensione batchSize.
  • Chiama optimizer.minimize() durante il calcolo della perdita del modello rispetto al batch di dati.
  • Può notificarti l'inizio e la fine di ogni epoca o lotto. 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à accodate nel ciclo di eventi JS possano essere gestite in modo tempestivo.

Per ulteriori informazioni, vedere 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 si adattano completamente alla memoria o vengono trasmessi in streaming, puoi addestrare un modello chiamando fitDataset() , che accetta un oggetto Dataset . Ecco lo stesso codice di addestramento ma con un set di dati che esegue il wrapping di una funzione del 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() .

Prevedere nuovi dati

Una volta che il modello è stato addestrato, 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 di base

In precedenza, abbiamo menzionato che esistono due modi per addestrare un modello di apprendimento automatico 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 pronte all'uso come l'inizializzazione del peso, la serializzazione del modello, il monitoraggio della formazione, la portabilità e il controllo della sicurezza.

Potresti voler utilizzare l'API principale 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, leggi la sezione "API di base" nella guida Modelli e livelli .

Lo stesso modello scritto sopra utilizzando l'API Core è simile al seguente:

// 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, l'API Data funziona perfettamente anche con l'API Core. Riutilizziamo il set di dati che abbiamo definito in precedenza nella sezione model.fitDataset() , che esegue il mescolamento e il raggruppamento in 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);

Alleniamo 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 sopra è la ricetta standard per il training di un modello con l'API Core:

  • Ripeti il ​​numero di epoche.
  • All'interno di ogni epoca, scorre i tuoi batch di dati. Quando si utilizza un Dataset , dataset.forEachAsync() è un modo conveniente per scorrere i batch.
  • Per ogni batch, chiama optimizer.minimize(f) , che esegue f e riduce al minimo il suo output calcolando i gradienti rispetto alle quattro variabili che abbiamo definito in precedenza.
  • f calcola la perdita. Richiama una delle funzioni di perdita predefinite utilizzando la previsione del modello e il valore reale.