Voltar para o Blog

JavaScript e Machine Learning: TensorFlow.js Está Democratizando a IA

Olá HaWkers, imagine treinar um modelo de Machine Learning e executá-lo diretamente no navegador, sem backend Python, sem servidores pesados. Parece futurista? É realidade com TensorFlow.js.

Você, desenvolvedor JavaScript, agora tem acesso a capacidades de IA que antes eram exclusivas de cientistas de dados. Vamos explorar como isso funciona, casos de uso reais e código prático que você pode rodar hoje.

Por Que Machine Learning no Navegador?

Vantagens do TensorFlow.js:

  1. Privacidade: Dados nunca saem do dispositivo do usuário
  2. Latência zero: Sem roundtrip para servidor
  3. Custo: Processamento distribuído nos clientes (não seu servidor)
  4. Acessibilidade: Qualquer dev JavaScript pode começar
  5. Multiplataforma: Browser, Node.js, React Native, Electron

Casos de uso reais em 2025:

  • Filtros de câmera em tempo real (Instagram, Snapchat)
  • Transcrição de áudio offline (Zoom, Meet)
  • Detecção de fraude em pagamentos
  • Recomendações personalizadas sem enviar dados
  • Acessibilidade (legendas, tradução em tempo real)

Setup Básico: Seu Primeiro Modelo

Vamos criar um detector de sentimento de texto — classifica se uma frase é positiva ou negativa.

Instalação:

npm install @tensorflow/tfjs
# Ou via CDN:
# <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>

Modelo pré-treinado simples:

import * as tf from '@tensorflow/tfjs';

// 1. Criar modelo sequencial simples
const model = tf.sequential({
  layers: [
    // Input: Texto convertido em números (embedding)
    tf.layers.dense({ inputShape: [100], units: 16, activation: 'relu' }),

    // Hidden layer
    tf.layers.dense({ units: 8, activation: 'relu' }),

    // Output: 2 classes (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. Treinar com dados
async function trainModel(reviews, labels) {
  // reviews: array de textos
  // labels: array de [0, 1] ou [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 treinado!');
}

// 4. Fazer predições
function predict(text) {
  // Converter texto em vetor 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'
  };
}

// Exemplo de uso
const result = predict('Adorei este produto, muito bom!');
console.log(result);
// { positive: 0.89, negative: 0.11, sentiment: 'Positivo' }

AI Technology

Caso Prático: Reconhecimento de Imagens em Tempo Real

Vamos usar um modelo pré-treinado (MobileNet) para classificar imagens da 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. Carregar modelo (download automático)
    console.log('Carregando MobileNet...');
    this.model = await mobilenet.load();
    console.log('✓ Modelo carregado!');

    // 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. Fazer predição em tempo 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); // Classifica a cada 1 segundo
  }
}

// HTML correspondente
/*
<video id="webcam" autoplay></video>
<div id="results"></div>

<script>
  const classifier = new ImageClassifier();
  classifier.init().then(() => {
    classifier.classifyLoop();
  });
</script>
*/

Resultado: Aplicação identifica objetos na câmera sem enviar dados para servidor!

Transfer Learning: Treinando Seu Próprio Classificador

E se você quiser classificar coisas específicas? Use Transfer Learning — pegue modelo pré-treinado e ajuste para seu caso.

Exemplo: Classificador de poses (exercícios 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 = ['agachamento', 'flexao', 'prancha', 'pulo'];
  }

  async loadBaseModel() {
    // Carregar MobileNet sem a última camada
    const mobilenet = await tf.loadLayersModel(
      'https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_0.25_224/model.json'
    );

    // Remover última camada (classificação genérica)
    const layer = mobilenet.getLayer('conv_pw_13_relu');
    this.baseModel = tf.model({
      inputs: mobilenet.inputs,
      outputs: layer.output
    });
  }

  createCustomModel() {
    // Adicionar novas camadas para suas classes
    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]

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

    // Converter labels para one-hot
    const ys = tf.oneHot(tf.tensor1d(labels, 'int32'), this.classes.length);

    // Treinar apenas as novas camadas
    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();

// Coletar dados (tirar fotos de cada exercício)
const agachamentoImgs = [img1, img2, img3]; // 3 exemplos
const flexaoImgs = [img4, img5, img6];
// ... mais exemplos

const allImages = [...agachamentoImgs, ...flexaoImgs, ...];
const labels = [0, 0, 0, 1, 1, 1, ...]; // Índices das classes

await classifier.train(allImages, labels);

// Fazer predição em nova imagem
const result = await classifier.predict(newImageElement);
console.log(result);
// {
//   class: 'agachamento',
//   confidence: '94.32%',
//   allProbabilities: [...]
// }

Performance: Aceleração GPU

TensorFlow.js usa WebGL para computação na GPU — crucial para modelos grandes.

Otimizações importantes:

// 1. Usar tf.tidy() para gerenciar memória
function processData(input) {
  return tf.tidy(() => {
    // Todos os tensors criados aqui são liberados automaticamente
    const normalized = input.div(255);
    const reshaped = normalized.reshape([1, 224, 224, 3]);
    return model.predict(reshaped);
  });
}

// 2. Batch processing para múltiplas imagens
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. Quantização para modelos menores
async function loadQuantizedModel() {
  // Modelo com 4x menos peso
  const model = await tf.loadGraphModel(
    'https://example.com/model_quantized/model.json'
  );

  return model;
}

// 4. Web Workers para não 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 Avançados

1. Detecção 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 contém 17 pontos (nariz, olhos, ombros, etc)
const nose = pose.keypoints.find(kp => kp.part === 'nose');
console.log(`Nariz em: x=${nose.position.x}, y=${nose.position.y}`);

2. Segmentação de Pessoas (BodyPix):

import * as bodyPix from '@tensorflow-models/body-pix';

const net = await bodyPix.load();

const segmentation = await net.segmentPerson(video);

// Aplicar blur no fundo (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. Reconhecimento de Fala:

import * as speechCommands from '@tensorflow-models/speech-commands';

const recognizer = speechCommands.create('BROWSER_FFT');

await recognizer.ensureModelLoaded();

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

Limitações e Considerações

O que TensorFlow.js NÃO é ideal para:

  • Treinamento de modelos gigantes (GPT, DALL-E)
  • Processamento batch massivo (milhões de imagens)
  • Pesquisa de ML de ponta

Ideal para:

  • Inferência em tempo real no cliente
  • Modelos pequenos/médios (<50MB)
  • Protótipos rápidos
  • Aplicações que precisam de privacidade

Trade-offs:

  • Performance: ~2-5x mais lento que Python/C++ nativo
  • Tamanho: Modelos precisam ser leves para web
  • Compatibilidade: Nem todas APIs Python estão disponíveis

O Futuro do ML no JavaScript

Tendências 2025:

  • WebGPU para performance ainda melhor
  • Modelos cada vez menores (técnicas de compressão)
  • Edge computing (ML em IoT devices com JS)
  • Integração com frameworks (React, Vue components com ML)

Se você quer explorar mais sobre como JavaScript está se expandindo para áreas inovadoras, veja JavaScript e o Mundo do IoT: Integrando a Web ao Ambiente Físico.

Bora pra cima! 🦅

📚 Quer Aprofundar Seus Conhecimentos em JavaScript?

Este artigo cobriu Machine Learning com JavaScript, mas há muito mais para explorar no mundo do desenvolvimento moderno.

Desenvolvedores que investem em conhecimento sólido e estruturado tendem a ter mais oportunidades no mercado.

Material de Estudo Completo

Se você quer dominar JavaScript do básico ao avançado, preparei um guia completo:

Opções de investimento:

  • R$9,90 (pagamento único)

👉 Conhecer o Guia JavaScript

💡 Material atualizado com as melhores práticas do mercado

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário