JavaScript y Machine Learning: TensorFlow.js Está Democratizando la IA
Hola HaWkers, imagina entrenar un modelo de Machine Learning y ejecutarlo directamente en el navegador, sin backend Python, sin servidores pesados. ¿Parece futurista? Es realidad con TensorFlow.js.
Tú, desarrollador JavaScript, ahora tienes acceso a capacidades de IA que antes eran exclusivas de científicos de datos. Vamos a explorar cómo funciona, casos de uso reales y código práctico que puedes ejecutar hoy.
¿Por Qué Machine Learning en el Navegador?
Ventajas del TensorFlow.js:
- Privacidad: Datos nunca salen del dispositivo del usuario
- Latencia cero: Sin roundtrip para servidor
- Costo: Procesamiento distribuido en los clientes (no tu servidor)
- Accesibilidad: Cualquier dev JavaScript puede comenzar
- Multiplataforma: Browser, Node.js, React Native, Electron
Casos de uso reales en 2025:
- Filtros de cámara en tiempo real (Instagram, Snapchat)
- Transcripción de audio offline (Zoom, Meet)
- Detección de fraude en pagos
- Recomendaciones personalizadas sin enviar datos
- Accesibilidad (subtítulos, traducción en tiempo real)
Setup Básico: Tu Primer Modelo
Vamos a crear un detector de sentimiento de texto — clasifica si una frase es positiva o negativa.
Instalación:
npm install @tensorflow/tfjs
# O via CDN:
# <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>Modelo preentrenado simple:
import * as tf from '@tensorflow/tfjs';
// 1. Crear modelo secuencial simple
const model = tf.sequential({
layers: [
// Input: Texto convertido en números (embedding)
tf.layers.dense({ inputShape: [100], units: 16, activation: 'relu' }),
// Hidden layer
tf.layers.dense({ units: 8, activation: 'relu' }),
// Output: 2 clases (positivo/negativo)
tf.layers.dense({ units: 2, activation: 'softmax' })
]
});
// 2. Compilar modelo
model.compile({
optimizer: tf.train.adam(0.001),
loss: 'categoricalCrossentropy',
metrics: ['accuracy']
});
// 3. Entrenar con datos
async function trainModel(reviews, labels) {
// reviews: array de textos
// labels: array de [0, 1] o [1, 0]
const xs = tf.tensor2d(reviews);
const ys = tf.tensor2d(labels);
await model.fit(xs, ys, {
epochs: 50,
batchSize: 32,
validationSplit: 0.2,
callbacks: {
onEpochEnd: (epoch, logs) => {
console.log(`Epoch ${epoch}: loss = ${logs.loss.toFixed(4)}`);
}
}
});
console.log('✓ ¡Modelo entrenado!');
}
// 4. Hacer predicciones
function predict(text) {
// Convertir texto en vector numérico (simplificado)
const vector = textToVector(text);
const input = tf.tensor2d([vector]);
const prediction = model.predict(input);
const scores = prediction.dataSync();
return {
positive: scores[0],
negative: scores[1],
sentiment: scores[0] > scores[1] ? 'Positivo' : 'Negativo'
};
}
// Ejemplo de uso
const result = predict('¡Me encantó este producto, muy bueno!');
console.log(result);
// { positive: 0.89, negative: 0.11, sentiment: 'Positivo' }
Caso Práctico: Reconocimiento de Imágenes en Tiempo Real
Vamos a usar un modelo preentrenado (MobileNet) para clasificar imágenes de la webcam.
import * as tf from '@tensorflow/tfjs';
import * as mobilenet from '@tensorflow-models/mobilenet';
class ImageClassifier {
constructor() {
this.model = null;
this.video = null;
}
async init() {
// 1. Cargar modelo (download automático)
console.log('Cargando MobileNet...');
this.model = await mobilenet.load();
console.log('✓ ¡Modelo cargado!');
// 2. Configurar webcam
this.video = document.getElementById('webcam');
const stream = await navigator.mediaDevices.getUserMedia({
video: { width: 640, height: 480 }
});
this.video.srcObject = stream;
await new Promise(resolve => {
this.video.onloadedmetadata = resolve;
});
this.video.play();
}
async classify() {
if (!this.model || !this.video) return;
// 3. Hacer predicción en tiempo real
const predictions = await this.model.classify(this.video);
return predictions.map(p => ({
class: p.className,
probability: (p.probability * 100).toFixed(2) + '%'
}));
}
async classifyLoop() {
const resultsDiv = document.getElementById('results');
setInterval(async () => {
const predictions = await this.classify();
resultsDiv.innerHTML = predictions
.map(p => `<p>${p.class}: ${p.probability}</p>`)
.join('');
}, 1000); // Clasifica cada 1 segundo
}
}
// HTML correspondiente
/*
<video id="webcam" autoplay></video>
<div id="results"></div>
<script>
const classifier = new ImageClassifier();
classifier.init().then(() => {
classifier.classifyLoop();
});
</script>
*/Resultado: ¡Aplicación identifica objetos en la cámara sin enviar datos para servidor!
Transfer Learning: Entrenando Tu Propio Clasificador
¿Y si quieres clasificar cosas específicas? Usa Transfer Learning — toma modelo preentrenado y ajústalo para tu caso.
Ejemplo: Clasificador de poses (ejercicios físicos)
import * as tf from '@tensorflow/tfjs';
import * as mobilenet from '@tensorflow-models/mobilenet';
class PoseClassifier {
constructor() {
this.baseModel = null;
this.model = null;
this.classes = ['sentadilla', 'flexion', 'plancha', 'salto'];
}
async loadBaseModel() {
// Cargar MobileNet sin la última capa
const mobilenet = await tf.loadLayersModel(
'https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_0.25_224/model.json'
);
// Remover última capa (clasificación genérica)
const layer = mobilenet.getLayer('conv_pw_13_relu');
this.baseModel = tf.model({
inputs: mobilenet.inputs,
outputs: layer.output
});
}
createCustomModel() {
// Añadir nuevas capas para tus clases
const model = tf.sequential({
layers: [
tf.layers.flatten({
inputShape: this.baseModel.outputs[0].shape.slice(1)
}),
tf.layers.dense({
units: 128,
activation: 'relu',
kernelInitializer: 'varianceScaling'
}),
tf.layers.dropout({ rate: 0.5 }),
tf.layers.dense({
units: this.classes.length,
activation: 'softmax'
})
]
});
model.compile({
optimizer: tf.train.adam(0.0001),
loss: 'categoricalCrossentropy',
metrics: ['accuracy']
});
this.model = model;
}
async train(images, labels) {
// images: array de elementos <img>
// labels: array de índices [0, 1, 2, 3]
// Extraer features con modelo base
const features = tf.tidy(() => {
const imageTensors = images.map(img => {
return tf.browser.fromPixels(img)
.resizeBilinear([224, 224])
.toFloat()
.div(127.5)
.sub(1);
});
const batched = tf.stack(imageTensors);
return this.baseModel.predict(batched);
});
// Convertir labels para one-hot
const ys = tf.oneHot(tf.tensor1d(labels, 'int32'), this.classes.length);
// Entrenar apenas las nuevas capas
await this.model.fit(features, ys, {
epochs: 20,
batchSize: 32,
validationSplit: 0.2,
callbacks: {
onEpochEnd: (epoch, logs) => {
console.log(
`Epoch ${epoch + 1}: ` +
`loss = ${logs.loss.toFixed(4)}, ` +
`accuracy = ${logs.acc.toFixed(4)}`
);
}
}
});
features.dispose();
ys.dispose();
}
async predict(imageElement) {
const processed = tf.tidy(() => {
const img = tf.browser.fromPixels(imageElement)
.resizeBilinear([224, 224])
.toFloat()
.div(127.5)
.sub(1)
.expandDims(0);
const features = this.baseModel.predict(img);
return this.model.predict(features);
});
const probabilities = await processed.data();
const classIndex = processed.argMax(-1).dataSync()[0];
processed.dispose();
return {
class: this.classes[classIndex],
confidence: (probabilities[classIndex] * 100).toFixed(2) + '%',
allProbabilities: this.classes.map((name, i) => ({
name,
probability: (probabilities[i] * 100).toFixed(2) + '%'
}))
};
}
}
// Uso
const classifier = new PoseClassifier();
await classifier.loadBaseModel();
classifier.createCustomModel();
// Recolectar datos (tomar fotos de cada ejercicio)
const sentadillaImgs = [img1, img2, img3]; // 3 ejemplos
const flexionImgs = [img4, img5, img6];
// ... más ejemplos
const allImages = [...sentadillaImgs, ...flexionImgs, ...];
const labels = [0, 0, 0, 1, 1, 1, ...]; // Índices de las clases
await classifier.train(allImages, labels);
// Hacer predicción en nueva imagen
const result = await classifier.predict(newImageElement);
console.log(result);
// {
// class: 'sentadilla',
// confidence: '94.32%',
// allProbabilities: [...]
// }
Performance: Aceleración GPU
TensorFlow.js usa WebGL para computación en la GPU — crucial para modelos grandes.
Optimizaciones importantes:
// 1. Usar tf.tidy() para gestionar memoria
function processData(input) {
return tf.tidy(() => {
// Todos los tensors creados aquí son liberados automáticamente
const normalized = input.div(255);
const reshaped = normalized.reshape([1, 224, 224, 3]);
return model.predict(reshaped);
});
}
// 2. Batch processing para múltiples imágenes
async function classifyMultiple(images) {
const batch = tf.tidy(() => {
const tensors = images.map(img =>
tf.browser.fromPixels(img).resizeBilinear([224, 224])
);
return tf.stack(tensors);
});
const predictions = await model.predict(batch).array();
batch.dispose();
return predictions;
}
// 3. Cuantización para modelos menores
async function loadQuantizedModel() {
// Modelo con 4x menos peso
const model = await tf.loadGraphModel(
'https://example.com/model_quantized/model.json'
);
return model;
}
// 4. Web Workers para no bloquear UI
// worker.js
importScripts('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs');
self.onmessage = async (e) => {
const { imageData } = e.data;
const tensor = tf.browser.fromPixels(imageData);
const prediction = await model.predict(tensor);
self.postMessage({ prediction: await prediction.array() });
tensor.dispose();
prediction.dispose();
};Casos de Uso Avanzados
1. Detección de Poses (PoseNet):
import * as posenet from '@tensorflow-models/posenet';
const net = await posenet.load({
architecture: 'MobileNetV1',
outputStride: 16,
inputResolution: { width: 640, height: 480 },
multiplier: 0.75
});
const pose = await net.estimateSinglePose(video);
// pose.keypoints contiene 17 puntos (nariz, ojos, hombros, etc)
const nose = pose.keypoints.find(kp => kp.part === 'nose');
console.log(`Nariz en: x=${nose.position.x}, y=${nose.position.y}`);2. Segmentación de Personas (BodyPix):
import * as bodyPix from '@tensorflow-models/body-pix';
const net = await bodyPix.load();
const segmentation = await net.segmentPerson(video);
// Aplicar blur en el fondo (tipo Zoom)
const foregroundColor = { r: 0, g: 0, b: 0, a: 0 };
const backgroundColor = { r: 0, g: 0, b: 0, a: 255 };
const backgroundBlurAmount = 15;
const backgroundBlur = await bodyPix.blurBodyPart(
canvas,
video,
segmentation,
backgroundBlurAmount,
foregroundColor,
backgroundColor
);3. Reconocimiento de Voz:
import * as speechCommands from '@tensorflow-models/speech-commands';
const recognizer = speechCommands.create('BROWSER_FFT');
await recognizer.ensureModelLoaded();
// Escuchar comandos
recognizer.listen(result => {
const scores = result.scores;
const maxScore = Math.max(...scores);
const command = recognizer.wordLabels()[scores.indexOf(maxScore)];
console.log(`Comando detectado: ${command} (${(maxScore * 100).toFixed(2)}%)`);
}, {
includeSpectrogram: true,
probabilityThreshold: 0.75
});
Limitaciones y Consideraciones
Lo que TensorFlow.js NO es ideal para:
- Entrenamiento de modelos gigantes (GPT, DALL-E)
- Procesamiento batch masivo (millones de imágenes)
- Investigación de ML de punta
Ideal para:
- Inferencia en tiempo real en el cliente
- Modelos pequeños/medianos (<50MB)
- Prototipos rápidos
- Aplicaciones que necesitan privacidad
Trade-offs:
- Performance: ~2-5x más lento que Python/C++ nativo
- Tamaño: Modelos necesitan ser ligeros para web
- Compatibilidad: No todas las APIs Python están disponibles
El Futuro del ML en JavaScript
Tendencias 2025:
- WebGPU para performance aún mejor
- Modelos cada vez menores (técnicas de compresión)
- Edge computing (ML en IoT devices con JS)
- Integración con frameworks (React, Vue components con ML)
Si quieres explorar más sobre cómo JavaScript se está expandiendo a áreas innovadoras, ve JavaScript y el Mundo del IoT: Integrando la Web al Ambiente Físico.
¡Vamos a por ello! 🦅
¿Quieres Profundizar Tus Conocimientos en JavaScript?
Este artículo cubrió Machine Learning con JavaScript, pero hay mucho más para explorar en el mundo del desarrollo moderno.
Desarrolladores que invierten en conocimiento sólido y estructurado tienden a tener más oportunidades en el mercado.
Material de Estudio Completo
Si quieres dominar JavaScript del básico al avanzado, preparé una guía completa:
Opciones de inversión:
- $9.90 USD (pago único)
Material actualizado con las mejores prácticas del mercado

