Modelos de entrenamiento

Esta guía asume que ya ha leído la modelos y capas de guía.

En TensorFlow.js hay dos formas de entrenar un modelo de aprendizaje automático:

  1. mediante la API de capas con LayersModel.fit() o LayersModel.fitDataset() .
  2. mediante la API Core con Optimizer.minimize() .

Primero, veremos la API de capas, que es una API de nivel superior para construir y entrenar modelos. Luego, mostraremos cómo entrenar el mismo modelo usando la API Core.

Introducción

Un modelo de aprendizaje de máquina es una función con parámetros se pueden aprender que los mapas de una entrada a una salida deseada. Los parámetros óptimos se obtienen entrenando el modelo con datos.

La formación implica varios pasos:

  • Conseguir un lote de datos al modelo.
  • Pedirle al modelo que haga una predicción.
  • Comparando esa predicción con el valor "verdadero".
  • Decidir cuánto cambiar cada parámetro para que el modelo pueda hacer una mejor predicción en el futuro para ese lote.

Un modelo bien entrenado proporcionará un mapeo preciso desde la entrada hasta la salida deseada.

Parámetros del modelo

Definamos un modelo simple de 2 capas usando la API de capas:

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

Bajo el capó, los modelos tienen parámetros (a menudo denominado como pesos) que se pueden aprender mediante la formación en los datos. Imprimamos los nombres de los pesos asociados a este modelo y sus formas:

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

Obtenemos el siguiente resultado:

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

Hay 4 pesos en total, 2 por capa densa. Esto se espera ya que las capas densas representan una función que mapea la entrada tensor x a un tensor de salida y a través de la ecuación y = Ax + b donde A (el núcleo) y b (el sesgo) son parámetros de la capa densa.

NOTA: Por defecto capas densas incluyen un sesgo, pero se puede excluirlo especificando {useBias: false} en las opciones al crear una capa densa.

model.summary() es un método útil si se desea obtener una visión general de su modelo y ver el número total de parámetros:

Capa (tipo) Forma de salida Parámetro #
dense_Dense1 (Denso) [nulo, 32] 25120
dense_Dense2 (Denso) [nulo, 10] 330
Parámetros totales: 25450
Parámetros entrenables: 25450
Parámetros no entrenables: 0

Cada peso en el modelo se backend una Variable objeto. En TensorFlow.js, una Variable es un punto flotante Tensor con un método adicional assign() se utiliza para la actualización de sus valores. La API de capas inicializa automáticamente los pesos utilizando las mejores prácticas. En aras de la demostración, se podría sobrescribir los pesos llamando assign() en las variables subyacentes:

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

Optimizador, pérdida y métrica

Antes de realizar cualquier entrenamiento, debe decidir tres cosas:

  1. Un optimizador. El trabajo del optimizador es decidir cuánto cambiar cada parámetro en el modelo, dada la predicción del modelo actual. Al utilizar la API de capas, puede proporcionar un identificador de cadena de un optimizador existente (como 'sgd' o 'adam' ), o una instancia del Optimizer clase.
  2. Una función de pérdida. Un objetivo que el modelo intentará minimizar. Su objetivo es dar un número único de "cuán equivocada" fue la predicción del modelo. La pérdida se calcula en cada lote de datos para que el modelo pueda actualizar sus pesos. Al utilizar la API de capas, puede proporcionar un identificador de cadena de un función de pérdida existente (como 'categoricalCrossentropy' ), o cualquier función que toma un valor y una verdadera predicho y vuelve una pérdida. Vea una lista de las pérdidas disponibles en nuestros documentos de la API.
  3. Lista de métricas. De manera similar a las pérdidas, las métricas calculan un solo número, resumiendo qué tan bien está funcionando nuestro modelo. Las métricas generalmente se calculan sobre la totalidad de los datos al final de cada época. Por lo menos, queremos monitorear que nuestras pérdidas disminuyan con el tiempo. Sin embargo, a menudo queremos una métrica más amigable para los humanos, como la precisión. Al utilizar la API de capas, puede proporcionar un identificador de cadena de un existente métrica (como 'accuracy' ), o cualquier función que toma un valor y una verdadera predicho y vuelve una puntuación. Vea una lista de métricas disponibles en nuestros documentos de la API.

Cuando se haya decidido, compilar una LayersModel llamando model.compile() con las opciones proporcionadas:

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

Durante la compilación, el modelo realizará una validación para asegurarse de que las opciones que elija sean compatibles entre sí.

Capacitación

Hay dos maneras de entrenar a un LayersModel :

  • Usando model.fit() y proporcionar los datos como una gran tensor.
  • Usando model.fitDataset() y proporcionar los datos a través de un Dataset de objeto.

model.fit ()

Si sus ajustes de conjunto de datos en la memoria principal, y está disponible como un único tensor, se puede entrenar un modelo llamando al fit() método:

// 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);
 });

Bajo el capó, model.fit() puede hacer mucho por nosotros:

  • Divide los datos en un tren y un conjunto de validación, y utiliza el conjunto de validación para medir el progreso durante el entrenamiento.
  • Mezcla los datos pero solo después de la división. Para estar seguro, usted debe pre-barajar los datos antes de pasarla a fit() .
  • Divide la gran tensor de datos en los tensores más pequeñas de tamaño batchSize.
  • Pide optimizer.minimize() , mientras que el cálculo de la pérdida del modelo con respecto a la serie de datos.
  • Puede notificarle sobre el inicio y el final de cada época o lote. En nuestro caso, nos notifica al final de cada lote mediante el callbacks.onBatchEnd opción. Otras opciones incluyen: onTrainBegin , onTrainEnd , onEpochBegin , onEpochEnd y onBatchBegin .
  • Cede al hilo principal para garantizar que las tareas en cola en el bucle de eventos JS se puedan manejar de manera oportuna.

Para obtener más información, consulte la documentación de fit() . Tenga en cuenta que si elige usar la API principal, tendrá que implementar esta lógica usted mismo.

model.fitDataset ()

Si los datos no encaja completamente en la memoria, o está siendo transmitido, se puede entrenar un modelo llamando fitDataset() , que toma un Dataset de objetos. Aquí está el mismo código de entrenamiento pero con un conjunto de datos que envuelve una función de generador:

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);
});

Para obtener más información sobre los conjuntos de datos, consulte la documentación de model.fitDataset() .

Predecir nuevos datos

Una vez que el modelo ha sido entrenado, puede llamar model.predict() para hacer predicciones sobre datos no visto:

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

API central

Anteriormente, mencionamos que hay dos formas de entrenar un modelo de aprendizaje automático en TensorFlow.js.

La regla general es intentar usar la API de capas primero, ya que está modelada a partir de la API de Keras bien adoptada. La API de capas también ofrece varias soluciones listas para usar, como inicialización de peso, serialización de modelos, capacitación de monitoreo, portabilidad y verificación de seguridad.

Es posible que desee utilizar la API principal siempre que:

  • Necesita máxima flexibilidad o control.
  • Y no necesita serialización ni puede implementar su propia lógica de serialización.

Para obtener más información acerca de esta API, lea la sección "API Core" en el Modelos y Capas guía.

El mismo modelo que se escribió anteriormente con la API central se ve así:

// 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);
}

Además de la API de capas, la API de datos también funciona a la perfección con la API principal. Vamos a volver a utilizar el conjunto de datos que hemos definido anteriormente en la model.fitDataset () sección, que no arrastrando los pies, de dosificación para nosotros:

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);

Entrenemos el modelo:

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);
}

El código anterior es la receta estándar al entrenar un modelo con la API principal:

  • Haz un bucle sobre el número de épocas.
  • Dentro de cada época, recorra sus lotes de datos. Al utilizar un Dataset , dataset.forEachAsync() es una forma conveniente de bucle sobre sus lotes.
  • Para cada lote, llamada optimizer.minimize(f) , que ejecuta f y minimiza su salida mediante el cálculo de gradientes con respecto a las cuatro variables que definimos anteriormente.
  • f calcula la pérdida. Llama a una de las funciones de pérdida predefinidas utilizando la predicción del modelo y el valor real.