Aprendizado de máquina para desenvolvedores front-end com Tensorflow.js
Publicados: 2022-03-10O aprendizado de máquina geralmente parece pertencer ao domínio dos cientistas de dados e desenvolvedores de Python. No entanto, nos últimos dois anos, estruturas de código aberto foram criadas para torná-lo mais acessível em diferentes linguagens de programação, incluindo JavaScript. Neste artigo, usaremos o Tensorflow.js para explorar as diferentes possibilidades de uso do aprendizado de máquina no navegador por meio de alguns projetos de exemplo.
O que é aprendizado de máquina?
Antes de começarmos a mergulhar em algum código, vamos falar brevemente sobre o que é aprendizado de máquina, bem como alguns conceitos e terminologia principais.
Definição
Uma definição comum é que é a capacidade dos computadores aprenderem com os dados sem serem explicitamente programados.
Se compararmos com a programação tradicional, isso significa que permitimos que os computadores identifiquem padrões nos dados e gerem previsões sem que tenhamos que dizer exatamente o que procurar.
Tomemos o exemplo da detecção de fraudes. Não há critérios definidos para saber o que torna uma transação fraudulenta ou não; fraudes podem ser executadas em qualquer país, em qualquer conta, visando qualquer cliente, a qualquer momento, e assim por diante. Seria praticamente impossível rastrear tudo isso manualmente.
No entanto, usando dados anteriores sobre despesas fraudulentas coletadas ao longo dos anos, podemos treinar um algoritmo de aprendizado de máquina para entender padrões nesses dados para gerar um modelo que possa receber qualquer nova transação e prever a probabilidade de ser fraude ou não, sem dizendo exatamente o que procurar.
Conceitos Fundamentais
Para entender os exemplos de código a seguir, precisamos abordar alguns termos comuns primeiro.
Modelo
Quando você treina um algoritmo de aprendizado de máquina com um conjunto de dados, o modelo é a saída desse processo de treinamento. É um pouco como uma função que recebe novos dados como entrada e produz uma previsão como saída.
Rótulos e recursos
Rótulos e recursos estão relacionados aos dados que você alimenta um algoritmo no processo de treinamento.
Um rótulo representa como você classificaria cada entrada em seu conjunto de dados e como você a rotularia. Por exemplo, se nosso conjunto de dados fosse um arquivo CSV descrevendo animais diferentes, nossos rótulos poderiam ser palavras como “gato”, “cachorro” ou “cobra” (dependendo do que cada animal representa).
Os recursos, por outro lado, são as características de cada entrada em seu conjunto de dados. Para o nosso exemplo de animais, podem ser coisas como “bigodes, miados”, “brincalhão, latidos”, “réptil, desenfreado” e assim por diante.
Usando isso, um algoritmo de aprendizado de máquina poderá encontrar alguma correlação entre os recursos e seu rótulo que usará para previsões futuras.
Redes neurais
As redes neurais são um conjunto de algoritmos de aprendizado de máquina que tentam imitar a maneira como o cérebro funciona usando camadas de neurônios artificiais.
Não precisamos nos aprofundar sobre como eles funcionam neste artigo, mas se você quiser saber mais, aqui está um vídeo muito bom:
Agora que definimos alguns termos comumente usados no aprendizado de máquina, vamos falar sobre o que pode ser feito usando JavaScript e a estrutura Tensorflow.js.
Recursos
Três recursos estão disponíveis atualmente:
- Usando um modelo pré-treinado,
- Transferir aprendizado,
- Definindo, executando e usando seu próprio modelo.
Vamos começar com o mais simples.
1. Usando um modelo pré-treinado
Dependendo do problema que você está tentando resolver, pode haver um modelo já treinado com um conjunto de dados específico e para uma finalidade específica que você pode aproveitar e importar em seu código.
Por exemplo, digamos que estamos construindo um site para prever se uma imagem é uma foto de um gato. Um modelo popular de classificação de imagens é chamado MobileNet e está disponível como um modelo pré-treinado com o Tensorflow.js.
O código para isso ficaria mais ou menos assim:
<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/[email protected]"> </script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/[email protected]"> </script> </head> <body> <img 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>
Começamos importando o Tensorflow.js e o modelo MobileNet na cabeça do nosso 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/[email protected]"> </script>
Então, dentro do corpo, temos um elemento de imagem que será usado para previsões:
<img alt="cat laying down" src="cat.jpeg"/>
E por fim, dentro da tag script
, temos o código JavaScript que carrega o modelo MobileNet pré-treinado e classifica a imagem encontrada na tag image
. Ele retorna uma matriz de 3 previsões que são ordenadas por pontuação de probabilidade (o primeiro elemento é a melhor previsão).
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();
E é isso! É assim que você pode usar um modelo pré-treinado no navegador com o Tensorflow.js!
Nota : Se você quiser dar uma olhada no que mais o modelo MobileNet pode classificar, você pode encontrar uma lista das diferentes classes disponíveis no Github.
Uma coisa importante a saber é que carregar um modelo pré-treinado no navegador pode levar algum tempo (às vezes até 10 segundos), então você provavelmente vai querer pré-carregar ou adaptar sua interface para que os usuários não sejam afetados.
Se preferir usar o Tensorflow.js como um módulo NPM, você pode fazer isso importando o módulo desta forma:
import * as mobilenet from '@tensorflow-models/mobilenet';
Sinta-se à vontade para brincar com este exemplo no CodeSandbox.
Agora que vimos como usar um modelo pré-treinado, vamos ver o segundo recurso disponível: o aprendizado de transferência.
2. Transferência de Aprendizagem
O aprendizado de transferência é a capacidade de combinar um modelo pré-treinado com dados de treinamento personalizados. O que isso significa é que você pode aproveitar a funcionalidade de um modelo e adicionar suas próprias amostras sem precisar criar tudo do zero.
Por exemplo, um algoritmo foi treinado com milhares de imagens para criar um modelo de classificação de imagem e, em vez de criar o seu próprio, o aprendizado de transferência permite combinar novas amostras de imagem personalizadas com o modelo pré-treinado para criar um novo classificador de imagem. Esse recurso torna muito rápido e fácil ter um classificador mais personalizado.
Para fornecer um exemplo de como isso ficaria no código, vamos redirecionar nosso exemplo anterior e modificá-lo para que possamos classificar novas imagens.
Nota : O resultado final é o experimento abaixo que você pode tentar ao vivo aqui.

Abaixo estão alguns exemplos de código da parte mais importante desta configuração, mas se você precisar dar uma olhada no código inteiro, você pode encontrá-lo neste CodeSandbox.
Ainda precisamos começar importando Tensorflow.js e MobileNet, mas desta vez também precisamos adicionar um classificador KNN (k-neest neighbor):
<!-- 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>
A razão pela qual precisamos de um classificador é porque (em vez de usar apenas o módulo MobileNet) estamos adicionando amostras personalizadas que nunca vimos antes, então o classificador KNN nos permitirá combinar tudo e executar previsões nos dados combinados.
Em seguida, podemos substituir a imagem do gato por uma tag de video
para usar imagens do feed da câmera.
<video autoplay width="227" height="227"></video>
Por fim, precisaremos adicionar alguns botões na página que usaremos como rótulos para gravar algumas amostras de vídeo e iniciar as previsões.
<section> <button class="button">Left</button> <button class="button">Right</button> <button class="test-predictions">Test</button> </section>
Agora, vamos passar para o arquivo JavaScript onde vamos começar configurando algumas variáveis 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");
Neste exemplo em particular, queremos ser capazes de classificar a entrada da webcam entre nossa cabeça inclinada para a esquerda ou para a direita, então precisamos de duas classes rotuladas left
e right
.
O tamanho da imagem definido como 227 é o tamanho do elemento de vídeo em pixels. Com base nos exemplos do Tensorflow.js, esse valor precisa ser definido como 227 para corresponder ao formato dos dados com os quais o modelo MobileNet foi treinado. Para que ele possa classificar nossos novos dados, os últimos precisam se encaixar no mesmo formato.

Se você realmente precisar que ele seja maior, é possível, mas você terá que transformar e redimensionar os dados antes de alimentá-los para o classificador KNN.
Em seguida, estamos definindo o valor de K para 10. O valor de K no algoritmo KNN é importante porque representa o número de instâncias que levamos em consideração ao determinar a classe de nossa nova entrada.
Nesse caso, o valor 10 significa que, ao prever o rótulo para alguns novos dados, examinaremos os 10 vizinhos mais próximos dos dados de treinamento para determinar como classificar nossa nova entrada.
Finalmente, estamos recebendo o elemento de video
. Para a lógica, vamos começar carregando o modelo e o classificador:
async load() { const knn = knnClassifier.create(); const mobilenetModule = await mobilenet.load(); console.log("model loaded"); }
Então, vamos acessar o feed de vídeo:
navigator.mediaDevices .getUserMedia({ video: true, audio: false }) .then(stream => { video.srcObject = stream; video.width = IMAGE_SIZE; video.height = IMAGE_SIZE; });
Depois disso, vamos configurar alguns eventos de botão para registrar nossos dados de amostra:
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); } }
Vamos escrever nossa função que irá pegar as amostras de imagens da webcam, reformatá-las e combiná-las com o 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); }
E, finalmente, depois de reunirmos algumas imagens da webcam, podemos testar nossas previsões com o seguinte código:
logits = infer(); const res = await this.knn.predictClass(logits, TOPK); const prediction = classes[res.classIndex];
E, finalmente, você pode descartar os dados da webcam, pois não precisamos mais deles:
// Dispose image when done image.dispose(); if (logits != null) { logits.dispose(); }
Mais uma vez, se você quiser dar uma olhada no código completo, você pode encontrá-lo no CodeSandbox mencionado anteriormente.
3. Treinando um modelo no navegador
O último recurso é definir, treinar e executar um modelo inteiramente no navegador. Para ilustrar isso, vamos construir o exemplo clássico de reconhecimento de Íris.
Para isso, criaremos uma rede neural capaz de classificar Irises em três categorias: Setosa, Virginica e Versicolor, com base em um conjunto de dados de código aberto.
Antes de começarmos, aqui está um link para a demonstração ao vivo e aqui está o CodeSandbox se você quiser brincar com o código completo.
No centro de cada projeto de aprendizado de máquina está um conjunto de dados. Uma das primeiras etapas que precisamos realizar é dividir esse conjunto de dados em um conjunto de treinamento e um conjunto de teste.
A razão para isso é que vamos usar nosso conjunto de treinamento para treinar nosso algoritmo e nosso conjunto de teste para verificar a precisão de nossas previsões, para validar se nosso modelo está pronto para ser usado ou precisa ser ajustado.
Nota : Para facilitar, já dividi o conjunto de treinamento e o conjunto de teste em dois arquivos JSON que você encontra no CodeSanbox.
O conjunto de treinamento contém 130 itens e o conjunto de teste 14. Se você observar como esses dados se parecem, verá algo assim:
{ "sepal_length": 5.1, "sepal_width": 3.5, "petal_length": 1.4, "petal_width": 0.2, "species": "setosa" }
O que podemos ver são quatro características diferentes para o comprimento e a largura da sépala e da pétala, além de um rótulo para a espécie.
Para poder usar isso com o Tensorflow.js, precisamos moldar esses dados em um formato que o framework entenda, neste caso, para os dados de treinamento, será [130, 4]
para 130 amostras com quatro recursos por íris.
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] );
Em seguida, precisamos moldar nossos dados de saída também:
const output = tf.tensor2d(trainingSet.map(item => [ item.species === 'setosa' ? 1 : 0, item.species === 'virginica' ? 1 : 0, item.species === 'versicolor' ? 1 : 0 ]), [130,3])
Então, quando nossos dados estiverem prontos, podemos passar para a criação do 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' } ));
No exemplo de código acima, começamos instanciando um modelo sequencial, adicionamos uma camada de entrada e saída.
Os parâmetros que você pode ver usados dentro ( inputShape
, activation
e units
) estão fora do escopo desta postagem, pois podem variar dependendo do modelo que você está criando, do tipo de dados usado e assim por diante.
Quando nosso modelo estiver pronto, podemos treiná-lo com nossos dados:
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(); }
Se isso funcionar bem, você pode começar a substituir os dados de teste por entradas personalizadas do usuário.
Depois de chamarmos nossa função principal, a saída da previsão será semelhante a uma destas três opções:
[1,0,0] // Setosa [0,1,0] // Virginica [0,0,1] // Versicolor
A previsão retorna uma matriz de três números representando a probabilidade dos dados pertencerem a uma das três classes. O número mais próximo de 1 é a previsão mais alta.
Por exemplo, se a saída da classificação for [0.0002, 0.9494, 0.0503]
, o segundo elemento da matriz será o mais alto, portanto, o modelo previu que a nova entrada provavelmente será uma Virginica.
E é isso para uma rede neural simples no Tensorflow.js!
Falamos apenas de um pequeno conjunto de dados de Irises, mas se você quiser passar para conjuntos de dados maiores ou trabalhar com imagens, os passos serão os mesmos:
- Coletando os dados;
- Divisão entre conjunto de treinamento e teste;
- Reformatar os dados para que o Tensorflow.js possa entendê-los;
- Escolhendo seu algoritmo;
- Ajustando os dados;
- Previsão.
Se você deseja salvar o modelo criado para poder carregá-lo em outro aplicativo e prever novos dados, pode fazê-lo com a seguinte linha:
await model.save('file:///path/to/my-model'); // in Node.js
Nota : Para mais opções sobre como salvar um modelo, dê uma olhada neste recurso.
Limites
É isso! Acabamos de abordar os três principais recursos disponíveis atualmente usando o Tensorflow.js!
Antes de terminarmos, acho importante mencionar brevemente alguns dos limites do uso do aprendizado de máquina no frontend.
1. Desempenho
A importação de um modelo pré-treinado de uma fonte externa pode afetar o desempenho do seu aplicativo. Alguns modelos de detecção de objetos, por exemplo, têm mais de 10 MB, o que tornará significativamente mais lento o seu site. Certifique-se de pensar na experiência do usuário e otimizar o carregamento de seus ativos para melhorar seu desempenho percebido.
2. Qualidade dos dados de entrada
Se você construir um modelo do zero, terá que coletar seus próprios dados ou encontrar algum conjunto de dados de código aberto.
Antes de fazer qualquer tipo de processamento de dados ou tentar algoritmos diferentes, certifique-se de verificar a qualidade dos seus dados de entrada. Por exemplo, se você estiver tentando construir um modelo de análise de sentimentos para reconhecer emoções em pedaços de texto, certifique-se de que os dados que você está usando para treinar seu modelo sejam precisos e diversos. Se a qualidade dos dados usados for baixa, o resultado do seu treinamento será inútil.
3. Responsabilidade
Usar um modelo pré-treinado de código aberto pode ser muito rápido e sem esforço. No entanto, isso também significa que você nem sempre sabe como ele foi gerado, do que o conjunto de dados foi feito ou mesmo qual algoritmo foi usado. Alguns modelos são chamados de “caixas pretas”, o que significa que você realmente não sabe como eles previram uma determinada saída.
Dependendo do que você está tentando construir, isso pode ser um problema. Por exemplo, se você estiver usando um modelo de aprendizado de máquina para ajudar a detectar a probabilidade de alguém ter câncer com base em imagens de varredura, em caso de falso negativo (o modelo previu que uma pessoa não teve câncer quando na verdade teve), há poderia ser alguma responsabilidade legal real e você teria que ser capaz de explicar por que o modelo fez uma determinada previsão.
Resumo
Concluindo, usar JavaScript e estruturas como Tensorflow.js é uma ótima maneira de começar e aprender mais sobre machine learning. Mesmo que um aplicativo pronto para produção provavelmente deva ser construído em uma linguagem como o Python, o JavaScript o torna realmente acessível para os desenvolvedores brincarem com os diferentes recursos e obterem uma melhor compreensão dos conceitos fundamentais antes de seguir em frente e investir tempo em aprender outro Língua.
Neste tutorial, abordamos apenas o que era possível usando o Tensorflow.js, no entanto, o ecossistema de outras bibliotecas e ferramentas está crescendo. Estruturas mais especificadas também estão disponíveis, permitindo que você explore o uso de aprendizado de máquina com outros domínios, como música com Magenta.js, ou preveja a navegação do usuário em um site usando guess.js!
À medida que as ferramentas aumentam o desempenho, as possibilidades de criar aplicativos habilitados para aprendizado de máquina em JavaScript provavelmente serão cada vez mais empolgantes, e agora é um bom momento para aprender mais sobre isso, pois a comunidade está se esforçando para torná-lo acessível.
Recursos adicionais
Se você estiver interessado em aprender mais, aqui estão alguns recursos:
Outros frameworks e ferramentas
- ml5.js
- ml.js
- brain.js
- Keras.js
- PoseNet
- Parque infantil do Tensorflow
Exemplos, modelos e conjuntos de dados
- Modelos do Tensorflow.js
- Exemplos de Tensorflow.js
- Conjuntos de dados
Inspiração
- Máquina ensinável
- Experimentos de IA
- AIJS.rocks
- Criabilidade
Obrigado por ler!