Aprendizaje automático para desarrolladores front-end con Tensorflow.js

El aprendizaje automático a menudo parece pertenecer al ámbito de los científicos de datos y los desarrolladores de Python. Sin embargo, en los últimos años, se han creado marcos de código abierto para que sea más accesible en diferentes lenguajes de programación, incluido JavaScript. En este artículo, utilizaremos Tensorflow.js para explorar las diferentes posibilidades de usar el aprendizaje automático en el navegador a través de algunos proyectos de ejemplo.

¿Qué es el aprendizaje automático?

Antes de comenzar a sumergirnos en algún código, hablemos brevemente sobre qué es el aprendizaje automático, así como algunos conceptos básicos y terminología.

DEFINICIÓN

Una definición común es que las computadoras pueden aprender de los datos sin ser programados explícitamente.

Si lo comparamos con la programación tradicional, significa que permitimos que las computadoras identifiquen patrones en los datos y generen predicciones sin que tengamos que decirle exactamente qué buscar.

Tomemos el ejemplo de detección de fraude. No hay criterios establecidos para saber qué hace que una transacción sea fraudulenta o no; Los fraudes se pueden ejecutar en cualquier país, en cualquier cuenta, dirigidos a cualquier cliente, en cualquier momento, etc. Sería prácticamente imposible rastrear todo esto manualmente.

Sin embargo, utilizando datos previos sobre gastos fraudulentos reunidos a lo largo de los años, podemos entrenar un algoritmo de aprendizaje automático para comprender los patrones en estos datos para generar un modelo que pueda recibir cualquier nueva transacción y predecir la probabilidad de fraude o no, sin diciéndole exactamente qué buscar.

CONCEPTOS BÁSICOS

Para comprender los siguientes ejemplos de código, primero debemos cubrir algunos términos comunes.

Modelo

Cuando entrena un algoritmo de aprendizaje automático con un conjunto de datos, el modelo es el resultado de este proceso de entrenamiento. Es un poco como una función que toma datos nuevos como entrada y produce una predicción como salida.

Etiquetas y características

Las etiquetas y las características se relacionan con los datos que alimenta un algoritmo en el proceso de capacitación.

Una etiqueta representa cómo clasificaría cada entrada en su conjunto de datos y cómo la etiquetaría. Por ejemplo, si nuestro conjunto de datos era un archivo CSV que describe diferentes animales, nuestras etiquetas podrían ser palabras como «gato», «perro» o «serpiente» (dependiendo de lo que represente cada animal).

Las características, por otro lado, son las características de cada entrada en su conjunto de datos. Para nuestro ejemplo de animales, podrían ser cosas como «bigotes, maullidos», «juguetones, ladridos», «reptiles, rampantes», etc.

Con esto, un algoritmo de aprendizaje automático podrá encontrar alguna correlación entre las características y su etiqueta que utilizará para futuras predicciones.

Redes neuronales

Las redes neuronales son un conjunto de algoritmos de aprendizaje automático que intentan imitar la forma en que funciona el cerebro utilizando capas de neuronas artificiales.

Ahora que hemos definido algunos términos comúnmente utilizados en el aprendizaje automático, hablemos de lo que se puede hacer usando JavaScript y el marco Tensorflow.js.

CARACTERISTICAS

Actualmente hay tres funciones disponibles:

  1. Usando un modelo pre-entrenado ,
  2. Transferencia de aprendizaje ,
  3. Definir, ejecutar y usar su propio modelo .

Comencemos con el más simple.

1. Usando un modelo pre-entrenado

Dependiendo del problema que esté tratando de resolver, puede haber un modelo ya capacitado con un conjunto de datos específico y para un propósito específico que puede aprovechar e importar en su código.

Por ejemplo, supongamos que estamos construyendo un sitio web para predecir si una imagen es la imagen de un gato. Un modelo popular de clasificación de imágenes se llama MobileNet y está disponible como modelo pre-entrenado con Tensorflow.js.

El código para esto se vería así:

<html lang=»en»>
<head>
<meta charset=»UTF-8″>
<meta name=»viewport» content=»width=device-width, initial-scale=1.0″>
<meta http-equiv=»X-UA-Compatible» content=»ie=edge»>
<title>Cat detection</title>
<script src=»https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.1″> </script>
<script src=»https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@1.0.0″> </script>
</head>
<body>
<img id=»image» alt=»cat laying down» src=»cat.jpeg»/>

<script>
const img = document.getElementById(‘image’);

const predictImage = async () => {
console.log(«Model loading…»);
const model = await mobilenet.load();
console.log(«Model is loaded!»)

const predictions = await model.classify(img);
console.log(‘Predictions: ‘, predictions);
}
predictImage();
</script>
</body>
</html>

Comenzamos importando Tensorflow.js y el modelo MobileNet en la cabeza de nuestro HTML:

<script src=»https://cdnjs.cloudflare.com/ajax/libs/tensorflow/1.0.1/tf.js»> </script>
<script src=»https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@1.0.0″> </script>

Luego, dentro del cuerpo, tenemos un elemento de imagen que se utilizará para las predicciones:

<img id=»image» alt=»cat laying down» src=»cat.jpeg»/>

Y finalmente, dentro de la scriptetiqueta, tenemos el código JavaScript que carga el modelo MobileNet pre-entrenado y clasifica la imagen encontrada en la imageetiqueta. Devuelve un conjunto de 3 predicciones ordenadas por puntuación de probabilidad (el primer elemento es la mejor predicción).

const predictImage = async () => {
console.log(«Model loading…»);
const model = await mobilenet.load();
console.log(«Model is loaded!»)
const predictions = await model.classify(img);
console.log(‘Predictions: ‘, predictions);
}

predictImage();

¡Y eso es! ¡Esta es la forma en que puede usar un modelo previamente entrenado en el navegador con Tensorflow.js!

Algo importante que debe saber es que cargar un modelo previamente entrenado en el navegador puede llevar algo de tiempo (a veces hasta 10 segundos), por lo que probablemente desee precargar o adaptar su interfaz para que los usuarios no se vean afectados.

Si prefiere usar Tensorflow.js como módulo NPM, puede hacerlo importando el módulo de esta manera:

import * as mobilenet from ‘@tensorflow-models/mobilenet’;

Ahora que hemos visto cómo usar un modelo previamente entrenado, veamos la segunda característica disponible: transferencia de aprendizaje.

2. Transferencia de aprendizaje

El aprendizaje por transferencia es la capacidad de combinar un modelo pre-entrenado con datos de entrenamiento personalizados. Lo que esto significa es que puede aprovechar la funcionalidad de un modelo y agregar sus propias muestras sin tener que crear todo desde cero.

Por ejemplo, un algoritmo ha sido entrenado con miles de imágenes para crear un modelo de clasificación de imágenes, y en lugar de crear el suyo, el aprendizaje de transferencia le permite combinar nuevas muestras de imágenes personalizadas con el modelo pre-entrenado para crear un nuevo clasificador de imágenes. Esta característica hace que sea realmente rápido y fácil tener un clasificador más personalizado.

Para proporcionar un ejemplo de cómo se vería esto en el código, reutilicemos nuestro ejemplo anterior y modifíquelo para que podamos clasificar nuevas imágenes.

Todavía tenemos que comenzar importando Tensorflow.js y MobileNet, pero esta vez también necesitamos agregar un clasificador KNN (vecino k-más cercano):

<!– Load TensorFlow.js –>
<script src=»https://cdn.jsdelivr.net/npm/@tensorflow/tfjs»></script>
<!– Load MobileNet –>
<script src=»https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet»></script>
<!– Load KNN Classifier –>
<script src=»https://cdn.jsdelivr.net/npm/@tensorflow-models/knn-classifier»></script>

La razón por la que necesitamos un clasificador es porque (en lugar de usar solo el módulo MobileNet) estamos agregando muestras personalizadas que nunca antes había visto, por lo que el clasificador KNN nos permitirá combinar todo y ejecutar predicciones sobre los datos combinados.

Luego, podemos reemplazar la imagen del gato con una videoetiqueta para usar imágenes de la alimentación de la cámara.

<video autoplay id=»webcam» width=»227″ height=»227″></video>

Finalmente, necesitaremos agregar algunos botones en la página que usaremos como etiquetas para grabar algunas muestras de video y comenzar las predicciones.

<section>
<button class=»button»>Left</button>

<button class=»button»>Right</button>

<button class=»test-predictions»>Test</button>
</section>

Ahora, pasemos al archivo JavaScript donde comenzaremos configurando algunas variables importantes:

// Number of classes to classify
const NUM_CLASSES = 2;
// Labels for our classes
const classes = [«Left», «Right»];
// Webcam Image size. Must be 227.
const IMAGE_SIZE = 227;
// K value for KNN
const TOPK = 10;

const video = document.getElementById(«webcam»);

En este ejemplo en particular, queremos poder clasificar la entrada de la cámara web entre nuestra cabeza inclinada hacia la izquierda o hacia la derecha, por lo que necesitamos dos clases etiquetadas leftright.

El tamaño de la imagen establecido en 227 es el tamaño del elemento de video en píxeles. Según los ejemplos de Tensorflow.js, este valor debe establecerse en 227 para que coincida con el formato de los datos con los que se entrenó el modelo MobileNet. Para poder clasificar nuestros nuevos datos, este último debe ajustarse al mismo formato.

Luego, establecemos el valor de K en 10. El valor de K en el algoritmo KNN es importante porque representa el número de instancias que tenemos en cuenta al determinar la clase de nuestra nueva entrada.

En este caso, el valor de 10 significa que, al predecir la etiqueta para algunos datos nuevos, miraremos a los 10 vecinos más cercanos a partir de los datos de entrenamiento para determinar cómo clasificar nuestra nueva entrada.

Finalmente, estamos obteniendo el videoelemento. Para la lógica, comencemos cargando el modelo y el clasificador:

async load() {
const knn = knnClassifier.create();
const mobilenetModule = await mobilenet.load();
console.log(«model loaded»);
}

Luego, accedamos al video:

navigator.mediaDevices
.getUserMedia({ video: true, audio: false })
.then(stream => {
video.srcObject = stream;
video.width = IMAGE_SIZE;
video.height = IMAGE_SIZE;
});
Después de eso, configuremos algunos eventos de botón para registrar nuestros datos de muestra:

setupButtonEvents() {
for (let i = 0; i < NUM_CLASSES; i++) {
let button = document.getElementsByClassName(«button»)[i];

button.onmousedown = () => {
this.training = i;
this.recordSamples = true;
};
button.onmouseup = () => (this.training = -1);
}
}

Escribamos nuestra función que tomará las muestras de imágenes de la cámara web, las formateará y las combinará con el módulo MobileNet:

// Get image data from video element
const image = tf.browser.fromPixels(video);

let logits;
// ‘conv_preds’ is the logits activation of MobileNet.
const infer = () => this.mobilenetModule.infer(image, «conv_preds»);

// Train class if one of the buttons is held down
if (this.training != -1) {
logits = infer();

// Add current image to classifier
this.knn.addExample(logits, this.training);
}

Y finalmente, una vez que reunimos algunas imágenes de la cámara web, podemos probar nuestras predicciones con el siguiente código:

logits = infer();
const res = await this.knn.predictClass(logits, TOPK);
const prediction = classes[res.classIndex];

Y finalmente, puede deshacerse de los datos de la cámara web ya que ya no los necesitamos:

// Dispose image when done
image.dispose();
if (logits != null) {
logits.dispose();
}

Una vez más, si desea ver el código completo, puede encontrarlo en CodeSandbox mencionado anteriormente.

3. Entrenando a una modelo en el navegador

La última característica es definir, entrenar y ejecutar un modelo completamente en el navegador. Para ilustrar esto, vamos a construir el ejemplo clásico de reconocer Iris.

Para esto, crearemos una red neuronal que pueda clasificar los Iris en tres categorías: Setosa, Virginica y Versicolor, en base a un conjunto de datos de código abierto.

El núcleo de cada proyecto de aprendizaje automático es un conjunto de datos. Uno de los primeros pasos que debemos emprender es dividir este conjunto de datos en un conjunto de entrenamiento y un conjunto de prueba.

La razón de esto es que vamos a usar nuestro conjunto de entrenamiento para entrenar nuestro algoritmo y nuestro conjunto de prueba para verificar la precisión de nuestras predicciones, para validar si nuestro modelo está listo para ser usado o necesita ser ajustado.

Nota : Para hacerlo más fácil, ya dividí el conjunto de entrenamiento y el conjunto de prueba en dos archivos JSON que puede encontrar en CodeSanbox .

El conjunto de entrenamiento contiene 130 elementos y el conjunto de prueba 14. Si observa cómo se ven estos datos, verá algo como esto:

{
«sepal_length»: 5.1,
«sepal_width»: 3.5,
«petal_length»: 1.4,
«petal_width»: 0.2,
«species»: «setosa»
}

Lo que podemos ver son cuatro características diferentes para la longitud y el ancho del sépalo y el pétalo, así como una etiqueta para la especie.

Para poder usar esto con Tensorflow.js, necesitamos dar forma a estos datos en un formato que el marco entienda, en este caso, para los datos de entrenamiento, será [130, 4]para 130 muestras con cuatro características por iris.

import * as trainingSet from «training.json»;
import * as testSet from «testing.json»;

const trainingData = tf.tensor2d(
trainingSet.map(item => [
item.sepal_length,
item.sepal_width,
item.petal_length,
item.petal_width
]),
[130, 4]
);

const testData = tf.tensor2d(
testSet.map(item => [
item.sepal_length,
item.sepal_width,
item.petal_length,
item.petal_width
]),
[14, 4]
);

A continuación, necesitamos dar forma a nuestros datos de salida también:

const output = tf.tensor2d(trainingSet.map(item => [
item.species === ‘setosa’ ? 1 : 0,
item.species === ‘virginica’ ? 1 : 0,
item.species === ‘versicolor’ ? 1 : 0

]), [130,3])

Luego, una vez que nuestros datos estén listos, podemos pasar a crear el modelo:

const model = tf.sequential();

model.add(tf.layers.dense(
{
inputShape: 4,
activation: ‘sigmoid’,
units: 10
}
));

model.add(tf.layers.dense(
{
inputShape: 10,
units: 3,
activation: ‘softmax’
}
));

En el ejemplo de código anterior, comenzamos instanciando un modelo secuencial, agregamos una capa de entrada y salida.

Los parámetros que puede ver utilizados dentro ( inputShapeactivationunits) están fuera del alcance de esta publicación, ya que pueden variar según el modelo que esté creando, el tipo de datos utilizados, etc.

Una vez que nuestro modelo esté listo, podemos entrenarlo con nuestros datos:

async function train_data(){
for(let i=0;i<15;i++){
const res = await model.fit(trainingData, outputData,{epochs: 40});
}
}

async function main() {
await train_data();
model.predict(testSet).print();
}

Si esto funciona bien, puede comenzar a reemplazar los datos de prueba con entradas de usuario personalizadas.

Una vez que llamemos a nuestra función principal, el resultado de la predicción se verá como una de estas tres opciones:

[1,0,0] // Setosa
[0,1,0] // Virginica
[0,0,1] // Versicolor

La predicción devuelve una matriz de tres números que representan la probabilidad de que los datos pertenezcan a una de las tres clases. El número más cercano a 1 es la predicción más alta.

Por ejemplo, si el resultado de la clasificación es [0.0002, 0.9494, 0.0503], el segundo elemento de la matriz es el más alto, por lo que el modelo predijo que la nueva entrada probablemente sea Virginica.

¡Y eso es todo para una red neuronal simple en Tensorflow.js!

Solo hablamos de un pequeño conjunto de datos de Iris, pero si desea pasar a conjuntos de datos más grandes o trabajar con imágenes, los pasos serán los mismos:

  • Recolectando los datos;
  • División entre entrenamiento y conjunto de pruebas;
  • Reformatear los datos para que Tensorflow.js pueda entenderlos;
  • Escogiendo tu algoritmo;
  • Ajuste de los datos;
  • Predecir

Si desea guardar el modelo creado para poder cargarlo en otra aplicación y predecir nuevos datos, puede hacerlo con la siguiente línea:

await model.save(‘file:///path/to/my-model’); // in Node.js

LÍMITES

¡Eso es! ¡Acabamos de cubrir las tres características principales disponibles actualmente con Tensorflow.js!

Antes de terminar, creo que es importante mencionar brevemente algunos de los límites del uso del aprendizaje automático en la interfaz.

1. Rendimiento

Importar un modelo previamente entrenado desde una fuente externa puede tener un impacto en el rendimiento de su aplicación. Algunos modelos de detección de objetos, por ejemplo, tienen más de 10 MB, lo que ralentizará significativamente su sitio web. Asegúrese de pensar en su experiencia de usuario y optimice la carga de sus activos para mejorar su rendimiento percibido.

2. Calidad de los datos de entrada

Si crea un modelo desde cero, tendrá que recopilar sus propios datos o buscar algún conjunto de datos de código abierto.

Antes de realizar cualquier tipo de procesamiento de datos o probar diferentes algoritmos, asegúrese de verificar la calidad de sus datos de entrada. Por ejemplo, si está tratando de construir un modelo de análisis de sentimientos para reconocer las emociones en fragmentos de texto, asegúrese de que los datos que está utilizando para entrenar su modelo sean precisos y diversos. Si la calidad de los datos utilizados es baja, el resultado de su entrenamiento será inútil.

3. Responsabilidad

El uso de un modelo pre-entrenado de código abierto puede ser muy rápido y sin esfuerzo. Sin embargo, también significa que no siempre sabe cómo se generó, de qué estaba hecho el conjunto de datos o incluso qué algoritmo se utilizó. Algunos modelos se llaman «cajas negras», lo que significa que realmente no se sabe cómo predijeron un determinado resultado.

Dependiendo de lo que intente construir, esto puede ser un problema. Por ejemplo, si está utilizando un modelo de aprendizaje automático para ayudar a detectar la probabilidad de que alguien tenga cáncer en base a imágenes escaneadas, en caso de falso negativo (el modelo predijo que una persona no tenía cáncer cuando realmente lo tenía), hay podría ser una responsabilidad legal real y tendría que ser capaz de explicar por qué el modelo hizo una cierta predicción.

Resumen

En conclusión, usar JavaScript y marcos como Tensorflow.js es una excelente manera de comenzar y aprender más sobre el aprendizaje automático. A pesar de que una aplicación lista para producción probablemente debería estar construida en un lenguaje como Python, JavaScript hace que sea realmente accesible para que los desarrolladores jueguen con las diferentes características y comprendan mejor los conceptos fundamentales antes de continuar e invertir tiempo en aprender otro idioma.

En este tutorial, solo hemos cubierto lo que era posible usando Tensorflow.js, sin embargo, el ecosistema de otras bibliotecas y herramientas está creciendo. Marcos más específicos también están disponibles, lo que permite explorar el uso de la máquina de aprendizaje con otros ámbitos como la música con Magenta.js , o la predicción de la navegación del usuario en un sitio web utilizando guess.js !

A medida que las herramientas se vuelven más eficaces, es probable que las posibilidades de crear aplicaciones habilitadas para el aprendizaje automático en JavaScript sean cada vez más emocionantes, y ahora es un buen momento para aprender más sobre ello, ya que la comunidad está haciendo un esfuerzo para hacerlo accesible.