Clasificador de imágenes mediante aprendizaje por transferencia de TensorFlow.js

En este codelab, aprenderás a compilar una “Teachable Machine” sencilla, un clasificador de imágenes personalizado que entrenarás sobre la marcha en el navegador usando TensorFlow.js, una biblioteca de aprendizaje automático potente y flexible para JavaScript. Primero, cargarás y ejecutarás un popular modelo previamente entrenado, llamado MobileNet, para la clasificación de imágenes en el navegador. Luego, usarás una técnica llamada “aprendizaje por transferencia”, que inicia nuestro entrenamiento con el modelo MobileNet previamente entrenado y lo personaliza a fin de entrenarlo para tu aplicación.

Este codelab no abarcará la teoría detrás de la aplicación de Teachable Machine. Si te interesa obtener información al respecto, consulta este instructivo.

Qué aprenderás

  • Cómo cargar el modelo MobileNet previamente entrenado y hacer una predicción con datos nuevos
  • Cómo hacer predicciones con la cámara web
  • Cómo usar las activaciones intermedias de MobileNet para realizar el aprendizaje por transferencia en un nuevo conjunto de clases que defines en el momento con la cámara web

Comencemos.

Para completar este codelab, necesitarás lo siguiente:

  1. Una versión reciente de Chrome o de otro navegador actualizado
  2. Un editor de texto que se ejecute localmente en tu máquina o en la Web con servicios como CodePen o Glitch
  3. Conocimientos sobre HTML, CSS, JavaScript y las Herramientas para desarrolladores de Chrome (o las de tu navegador preferido)
  4. Comprensión conceptual de alto nivel de las redes neuronales (si necesitas una introducción o un repaso, te recomendamos mirar este video de 3blue1brown o este video sobre aprendizaje profundo en JavaScript de Ashi Krishnan)

Abre index.html en un editor y agrega este contenido:

<html>
  <head>
    <!-- Load the latest version of TensorFlow.js -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet"></script>
  </head>
  <body>
    <div id="console"></div>
    <!-- Add an image that we will use to test -->
    <img id="img" crossorigin src="https://i.imgur.com/JlUvsxa.jpg" width="227" height="227"/>
    <!-- Load index.js after the content of the page -->
    <script src="index.js"></script>
  </body>
</html>

A continuación, abre o crea un archivo index.js en un editor de código y, luego, incluye el siguiente código:

let net;

async function app() {
  console.log('Loading mobilenet..');

  // Load the model.
  net = await mobilenet.load();
  console.log('Successfully loaded model');

  // Make a prediction through the model on our image.
  const imgEl = document.getElementById('img');
  const result = await net.classify(imgEl);
  console.log(result);
}

app();

Para ejecutar la página web, abre index.html en un navegador web. Si usas Cloud Console, simplemente actualiza la página de vista previa.

Deberías ver una imagen de un perro y, en la Consola de JavaScript de las Herramientas para desarrolladores, las principales predicciones de MobileNet. Ten en cuenta que descargar el modelo puede tardar un poco. Ten paciencia.

¿Se clasificó correctamente la imagen?

Es importante mencionar que también funcionará en un teléfono celular.

Ahora, hagamos esto de forma más interactiva y en tiempo real. Configuremos la cámara web para que haga predicciones de imágenes que provengan de ella.

Primero, configura el elemento de video de la cámara web. Abre el archivo index.html, agrega la siguiente línea a la sección <body> y borra la etiqueta <img> que teníamos para cargar la imagen del perro:

<video autoplay playsinline muted id="webcam" width="224" height="224"></video>

Abre el archivo index.js y agrega el webcamElement a la parte superior del archivo.

const webcamElement = document.getElementById('webcam');

Ahora, en la función app() que agregaste antes, puedes quitar la predicción a través de la imagen y, en su lugar, crear un bucle infinito que realice predicciones mediante el elemento de cámara web.

async function app() {
  console.log('Loading mobilenet..');

  // Load the model.
  net = await mobilenet.load();
  console.log('Successfully loaded model');

  // Create an object from Tensorflow.js data API which could capture image
  // from the web camera as Tensor.
  const webcam = await tf.data.webcam(webcamElement);
  while (true) {
    const img = await webcam.capture();
    const result = await net.classify(img);

    document.getElementById('console').innerText = `
      prediction: ${result[0].className}\n
      probability: ${result[0].probability}
    `;
    // Dispose the tensor to release the memory.
    img.dispose();

    // Give some breathing room by waiting for the next animation frame to
    // fire.
    await tf.nextFrame();
  }
}

Si abres la consola en la página web, deberías ver una predicción de MobileNet con la probabilidad de cada fotograma recopilado en la cámara web.

Es posible que los datos no tengan sentido porque el conjunto de datos de ImageNet no se parece mucho a las imágenes que normalmente aparecerían en una cámara web. Para probarlo, puedes sostener una foto de un perro en tu teléfono frente a la cámara de tu laptop.

Ahora, hagamos que esto sea más útil. Crearemos un clasificador de objetos personalizado de 3 clases usando la cámara web sobre la marcha. Haremos una clasificación a través de MobileNet, pero esta vez usaremos una representación (activación) interna del modelo para una imagen de cámara web específica y la utilizaremos para la clasificación.

Usaremos un módulo llamado “Clasificador k‑vecinos más cercanos”, que nos permite colocar eficazmente imágenes de cámara web (de hecho, sus activaciones de MobileNet) en diferentes categorías (o “clases”) y, cuando el usuario solicita hacer una predicción, simplemente elegimos la clase que tenga la activación más similar a la categoría para la que estamos haciendo la predicción.

Agrega una importación del Clasificador KNN al final de las importaciones en la etiqueta <head> de index.html (necesitarás MobileNet de todos modos, por lo que no debes quitar esa importación):

...
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/knn-classifier"></script>
...

Agrega 3 botones para cada uno de los botones de index.html debajo del elemento de video. Estos botones se usarán para agregar imágenes de entrenamiento al modelo.

...
<button id="class-a">Add A</button>
<button id="class-b">Add B</button>
<button id="class-c">Add C</button>
...

En la parte superior de index.js, crea el siguiente clasificador:

const classifier = knnClassifier.create();

Actualiza la función de la app:

async function app() {
  console.log('Loading mobilenet..');

  // Load the model.
  net = await mobilenet.load();
  console.log('Successfully loaded model');

  // Create an object from Tensorflow.js data API which could capture image
  // from the web camera as Tensor.
  const webcam = await tf.data.webcam(webcamElement);

  // Reads an image from the webcam and associates it with a specific class
  // index.
  const addExample = async classId => {
    // Capture an image from the web camera.
    const img = await webcam.capture();

    // Get the intermediate activation of MobileNet 'conv_preds' and pass that
    // to the KNN classifier.
    const activation = net.infer(img, true);

    // Pass the intermediate activation to the classifier.
    classifier.addExample(activation, classId);

    // Dispose the tensor to release the memory.
    img.dispose();
  };

  // When clicking a button, add an example for that class.
  document.getElementById('class-a').addEventListener('click', () => addExample(0));
  document.getElementById('class-b').addEventListener('click', () => addExample(1));
  document.getElementById('class-c').addEventListener('click', () => addExample(2));

  while (true) {
    if (classifier.getNumClasses() > 0) {
      const img = await webcam.capture();

      // Get the activation from mobilenet from the webcam.
      const activation = net.infer(img, 'conv_preds');
      // Get the most likely class and confidence from the classifier module.
      const result = await classifier.predictClass(activation);

      const classes = ['A', 'B', 'C'];
      document.getElementById('console').innerText = `
        prediction: ${classes[result.label]}\n
        probability: ${result.confidences[result.label]}
      `;

      // Dispose the tensor to release the memory.
      img.dispose();
    }

    await tf.nextFrame();
  }
}

Ahora, cuando cargas la página index.html, puedes usar objetos comunes o gestos faciales y corporales a fin de capturar imágenes para cada una de las tres clases. Cada vez que haces clic en uno de los botones "Agregar", se agrega una imagen a esa clase como ejemplo de entrenamiento. Mientras haces esto, el modelo continúa realizando predicciones de imágenes que provienen de la cámara web y muestra los resultados en tiempo real.

Ahora, intenta agregar otra clase que no represente ninguna acción.

En este codelab, implementaste una aplicación web de aprendizaje automático simple con TensorFlow.js. Cargaste y utilizaste un modelo MobileNet previamente entrenado para clasificar imágenes de la cámara web. Luego, personalizaste el modelo para clasificar imágenes en tres categorías personalizadas.

Visita js.tensorflow.org para obtener más ejemplos y demostraciones con código a fin de ver cómo puedes usar TensorFlow.js en tus aplicaciones.