1. Antes de começar
O uso de modelos do TensorFlow.js cresceu exponencialmente nos últimos anos, e muitos desenvolvedores de JavaScript agora querem pegar modelos atuais e treinar novamente para trabalhar com dados personalizados exclusivos do setor deles. O ato de usar um modelo atual (geralmente chamado de modelo de base) em um domínio semelhante, mas diferente, é conhecido como aprendizado por transferência.
O aprendizado por transferência tem muitas vantagens em relação a começar com um modelo completamente em branco. Você pode reutilizar o conhecimento já aprendido com um modelo treinado anteriormente e precisa de menos exemplos do novo item que quer classificar. Além disso, o treinamento geralmente é muito mais rápido porque só é necessário treinar novamente as últimas camadas da arquitetura do modelo, em vez de toda a rede. Por isso, o aprendizado por transferência é muito adequado para o ambiente de navegador da Web, em que os recursos podem variar de acordo com o dispositivo de execução, mas também têm acesso direto aos sensores para facilitar a aquisição de dados.
Neste codelab, mostramos como criar um app da Web do zero, recriando o site popular Máquina que Aprende do Google. O site permite criar um app da Web funcional que qualquer usuário pode usar para reconhecer um objeto personalizado com apenas algumas imagens de exemplo da webcam. O site é propositalmente mantido minimalista para que você possa se concentrar nos aspectos de machine learning deste codelab. Assim como no site original da Máquina que Aprende, há muito espaço para aplicar sua experiência como desenvolvedor da Web e melhorar a UX.
Pré-requisitos
Este codelab foi escrito para desenvolvedores da Web que conhecem um pouco os modelos prontos do TensorFlow.js e o uso básico da API e querem começar a usar o aprendizado por transferência no TensorFlow.js.
- Neste laboratório, presumimos que você tem noções básicas de TensorFlow.js, HTML5, CSS e JavaScript.
Se você não conhece o Tensorflow.js, faça este curso sem custo financeiro para iniciantes primeiro. Ele não exige experiência com aprendizado de máquina ou TensorFlow.js e ensina tudo o que você precisa saber em etapas menores.
O que você vai aprender
- O que é o TensorFlow.js e por que você deve usá-lo no seu próximo app da Web.
- Como criar uma página da Web simplificada em HTML/CSS /JS que replica a experiência do usuário da Máquina que Aprende.
- Como usar o TensorFlow.js para carregar um modelo de base pré-treinado, especificamente o MobileNet, para gerar recursos de imagem que podem ser usados no aprendizado por transferência.
- Como coletar dados da webcam de um usuário para várias classes de dados que você quer reconhecer.
- Como criar e definir um perceptron multicamadas que usa os recursos da imagem e aprende a classificar novos objetos usando esses recursos.
Vamos começar a hackear…
O que é necessário
- É recomendável ter uma conta do Glitch.com para acompanhar, mas você também pode usar um ambiente de serviço da Web que se sinta à vontade para editar e executar.
2. O que é o TensorFlow.js?

O TensorFlow.js é uma biblioteca de machine learning de código aberto que pode ser executada em qualquer lugar que o JavaScript possa. Ela é baseada na biblioteca original do TensorFlow escrita em Python e tem como objetivo recriar essa experiência do desenvolvedor e o conjunto de APIs para o ecossistema JavaScript.
Onde ela pode ser usada?
Devido à portabilidade do JavaScript, agora é possível programar em uma linguagem e realizar aprendizado de máquina em todas as seguintes plataformas com facilidade:
- Lado do cliente no navegador da Web usando JavaScript simples
- Lado do servidor e até mesmo dispositivos IoT, como o Raspberry Pi, usando o Node.js
- Apps para computador usando Electron
- Apps nativos para dispositivos móveis usando o React Native
O TensorFlow.js também oferece suporte a vários back-ends em cada um desses ambientes (os ambientes reais baseados em hardware em que ele pode ser executado, como CPU ou WebGL, por exemplo). Um "back-end" nesse contexto não significa um ambiente do lado do servidor. O back-end para execução pode ser do lado do cliente em WebGL, por exemplo, para garantir a compatibilidade e manter tudo funcionando rapidamente. No momento, o TensorFlow.js é compatível com:
- Execução do WebGL na placa gráfica do dispositivo (GPU): essa é a maneira mais rápida de executar modelos maiores (mais de 3 MB) com aceleração de GPU.
- Execução do Web Assembly (WASM) na CPU: para melhorar a performance da CPU em todos os dispositivos, incluindo smartphones de gerações mais antigas, por exemplo. Isso é mais adequado para modelos menores (menos de 3 MB), que podem ser executados mais rapidamente na CPU com WASM do que com WebGL devido ao overhead de upload de conteúdo para um processador gráfico.
- Execução da CPU: o fallback será usado se nenhum dos outros ambientes estiver disponível. É o mais lento dos três, mas está sempre disponível.
Observação:você pode forçar um desses back-ends se souber em qual dispositivo vai executar ou deixar o TensorFlow.js decidir por você se não especificar isso.
Superpoderes do lado do cliente
Executar o TensorFlow.js no navegador da Web na máquina do cliente pode trazer vários benefícios que valem a pena considerar.
Privacidade
É possível treinar e classificar dados na máquina do cliente sem enviar informações para um servidor da Web de terceiros. Em alguns casos, isso pode ser um requisito para obedecer às leis locais, como o GDPR, ou ao processar dados que o usuário queira manter na máquina e não enviar a terceiros.
Velocidade
Como você não precisa enviar dados para um servidor remoto, a inferência (o ato de classificar os dados) pode ser mais rápida. Melhor ainda, você tem acesso direto aos sensores do dispositivo, como câmera, microfone, GPS, acelerômetro e muito mais, caso o usuário conceda acesso.
Alcance e escala
Com um clique, qualquer pessoa no mundo pode clicar em um link que você enviou, abrir a página da Web no navegador e usar o que você criou. Não é necessário ter uma configuração complexa do Linux do lado do servidor com drivers CUDA e muito mais apenas para usar o sistema de aprendizado de máquina.
Custo
Sem servidores, a única coisa que você precisa pagar é uma CDN para hospedar seus arquivos HTML, CSS, JS e de modelo. O custo de uma CDN é muito menor do que manter um servidor (possivelmente com uma placa gráfica conectada) funcionando 24 horas por dia, 7 dias por semana.
Recursos do lado do servidor
O uso da implementação do TensorFlow.js em Node.js permite os seguintes recursos.
Suporte completo ao CUDA
No lado do servidor, para aceleração da placa gráfica, instale os drivers NVIDIA CUDA para permitir que o TensorFlow funcione com a placa gráfica (ao contrário do navegador, que usa WebGL e não precisa de instalação). No entanto, com suporte total ao CUDA, é possível aproveitar ao máximo as capacidades de nível inferior da placa de vídeo, o que resulta em tempos de treinamento e inferência mais rápidos. O desempenho é igual à implementação do TensorFlow em Python, já que ambos compartilham o mesmo back-end em C++.
Tamanho do modelo
Para modelos de ponta de pesquisa, você pode estar trabalhando com modelos muito grandes, talvez gigabytes de tamanho. No momento, não é possível executar esses modelos no navegador da web devido às limitações de uso da memória por guia do navegador. Para executar esses modelos maiores, use o Node.js no seu próprio servidor com as especificações de hardware necessárias para executar um modelo desse tipo com eficiência.
IOT
O Node.js é compatível com computadores de placa única conhecidos, como o Raspberry Pi (em inglês). Isso significa que você também pode executar modelos do TensorFlow.js nesses dispositivos.
Velocidade
O Node.js é escrito em JavaScript, o que significa que ele se beneficia da compilação just-in-time. Isso significa que você pode ter ganhos de performance ao usar o Node.js, já que ele será otimizado no tempo de execução, principalmente para qualquer pré-processamento que você esteja fazendo. Um ótimo exemplo disso pode ser visto neste estudo de caso, que mostra como a Hugging Face usou o Node.js para aumentar o desempenho do modelo de processamento de linguagem natural em duas vezes.
Agora que você entende o básico do TensorFlow.js, onde ele pode ser executado e alguns dos benefícios, vamos começar a fazer coisas úteis com ele.
3. Aprendizado por transferência
O que é aprendizado por transferência?
O aprendizado por transferência envolve usar o conhecimento já adquirido para aprender algo diferente, mas semelhante.
Nós, humanos, fazemos isso o tempo todo. Você tem uma vida inteira de experiências contidas no seu cérebro que podem ajudar a reconhecer coisas novas que nunca viu antes. Por exemplo, considere este salgueiro-chorão:

Dependendo de onde você está no mundo, talvez nunca tenha visto esse tipo de árvore.
Mas se eu pedir para você me dizer se há salgueiros na nova imagem abaixo, provavelmente você vai identificá-los rapidamente, mesmo que estejam em um ângulo diferente e um pouco diferentes da original que mostrei.

Você já tem um monte de neurônios no cérebro que sabem identificar objetos parecidos com árvores e outros que são bons em encontrar linhas retas longas. Você pode reutilizar esse conhecimento para classificar rapidamente um salgueiro-chorão, que é um objeto semelhante a uma árvore com muitos galhos verticais longos e retos.
Da mesma forma, se você tiver um modelo de aprendizado de máquina já treinado em um domínio, como reconhecimento de imagens, poderá reutilizá-lo para realizar uma tarefa diferente, mas relacionada.
Você pode fazer o mesmo com um modelo avançado como o MobileNet, que é um modelo de pesquisa muito conhecido que pode realizar o reconhecimento de imagens em mil tipos diferentes de objetos. De cachorros a carros, ele foi treinado em um enorme conjunto de dados conhecido como ImageNet, que tem milhões de imagens rotuladas.
Nesta animação, é possível ver o grande número de camadas que ele tem neste modelo MobileNet V1:

Durante o treinamento, esse modelo aprendeu a extrair recursos comuns importantes para todos esses 1.000 objetos. Muitos dos recursos de nível inferior usados para identificar esses objetos também podem ser úteis para detectar novos objetos que ele nunca viu antes. Afinal, tudo é apenas uma combinação de linhas, texturas e formas.
Vamos analisar uma arquitetura tradicional de rede neural convolucional (CNN, na sigla em inglês), semelhante à MobileNet, e ver como o aprendizado por transferência pode aproveitar essa rede treinada para aprender algo novo. A imagem abaixo mostra a arquitetura típica de uma CNN que, neste caso, foi treinada para reconhecer algarismos manuscritos de 0 a 9:

Se você pudesse separar as camadas pré-treinadas de nível inferior de um modelo treinado existente, como o mostrado à esquerda, das camadas de classificação perto do final do modelo mostrado à direita (às vezes chamado de cabeçalho de classificação do modelo), seria possível usar as camadas de nível inferior para produzir recursos de saída para qualquer imagem com base nos dados originais em que ela foi treinada. Esta é a mesma rede sem o cabeçalho de classificação:

Supondo que a nova coisa que você está tentando reconhecer também possa usar esses recursos de saída que o modelo anterior aprendeu, há uma boa chance de que eles possam ser reutilizados para uma nova finalidade.
No diagrama acima, esse modelo hipotético foi treinado com dígitos. Portanto, talvez o que foi aprendido sobre dígitos também possa ser aplicado a letras como a, b e c.
Agora você pode adicionar um novo cabeçalho de classificação que tenta prever a, b ou c, conforme mostrado:

Aqui, as camadas de nível inferior são congeladas e não são treinadas. Apenas o novo cabeçalho de classificação será atualizado para aprender com os recursos fornecidos pelo modelo pré-treinado e fragmentado à esquerda.
Esse ato é conhecido como aprendizado por transferência, e é o que a Máquina que Aprende faz nos bastidores.
Além disso, ao treinar apenas o perceptron multicamadas no final da rede, o processo é muito mais rápido do que se você tivesse que treinar toda a rede do zero.
Mas como acessar subpartes de um modelo? Confira na próxima seção.
4. TensorFlow Hub: modelos básicos
Encontrar um modelo de base adequado para usar
Para modelos de pesquisa mais avançados e conhecidos, como o MobileNet, acesse o TensorFlow Hub e filtre os modelos adequados para o TensorFlow.js que usam a arquitetura MobileNet v3 para encontrar resultados como os mostrados aqui:

Alguns desses resultados são do tipo "classificação de imagem" (detalhada na parte superior esquerda de cada card de modelo), e outros são do tipo "vetor de recursos de imagem".
Esses resultados de vetor de recursos de imagem são essencialmente as versões pré-cortadas do MobileNet que podem ser usadas para receber os vetores de recursos de imagem em vez da classificação final.
Modelos como esse são chamados de "modelos de base", que podem ser usados para realizar o aprendizado por transferência da mesma maneira mostrada na seção anterior, adicionando um novo cabeçalho de classificação e treinando-o com seus próprios dados.
Em seguida, verifique em qual formato do TensorFlow.js o modelo base de interesse foi lançado. Se você abrir a página de um desses modelos MobileNet v3 de vetor de recursos, poderá ver na documentação JS que ele está na forma de um modelo de gráfico com base no snippet de código de exemplo na documentação, que usa tf.loadGraphModel().

Também é importante observar que, se você encontrar um modelo no formato de camadas em vez do formato de gráfico, poderá escolher quais camadas congelar e quais descongelar para treinamento. Isso pode ser muito útil ao criar um modelo para uma nova tarefa, que geralmente é chamado de "modelo de transferência". Por enquanto, use o tipo de modelo de gráfico padrão para este tutorial, que é como a maioria dos modelos do TF Hub são implantados. Para saber mais sobre como trabalhar com modelos de camadas, confira o curso zero to hero TensorFlow.js (em inglês).
Vantagens do aprendizado por transferência
Quais são as vantagens de usar o aprendizado por transferência em vez de treinar toda a arquitetura do modelo do zero?
Primeiro, o tempo de treinamento é uma vantagem fundamental de usar uma abordagem de aprendizado por transferência, já que você tem um modelo base treinado para criar.
Em segundo lugar, você pode mostrar muito menos exemplos da nova coisa que está tentando classificar devido ao treinamento que já ocorreu.
Isso é ótimo se você tiver pouco tempo e recursos para coletar dados de exemplo do que quer classificar e precisar criar um protótipo rapidamente antes de coletar mais dados de treinamento para torná-lo mais robusto.
Devido à necessidade de menos dados e à velocidade de treinamento de uma rede menor, o aprendizado por transferência exige menos recursos. Isso o torna muito adequado para o ambiente do navegador, levando apenas dezenas de segundos em uma máquina moderna, em vez de horas, dias ou semanas para o treinamento de modelo completo.
Muito bem! Agora que você já sabe o que é o aprendizado por transferência, é hora de criar sua própria versão da Máquina que Aprende. Vamos começar!
5. Configurar para programar
O que é necessário
- Um navegador da Web moderno.
- Conhecimento básico de HTML, CSS, JavaScript e Chrome DevTools (visualização da saída do console).
Vamos programar
Modelos de boilerplate para começar foram criados para Glitch.com ou Codepen.io. Basta clonar um dos modelos como estado de base para este codelab com apenas um clique.
No Glitch, clique no botão remix this para fazer um fork e criar um novo conjunto de arquivos que podem ser editados.
Se preferir, no Codepen, clique em fork no canto inferior direito da tela.
Esse esqueleto muito simples fornece os seguintes arquivos:
- Página HTML (index.html)
- Folha de estilo (style.css)
- Arquivo para escrever nosso código JavaScript (script.js)
Para sua conveniência, há uma importação adicionada no arquivo HTML para a biblioteca TensorFlow.js. Esta é a aparência dela:
index.html
<!-- Import TensorFlow.js library -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js" type="text/javascript"></script>
Alternativa: use seu editor da Web preferido ou trabalhe localmente
Se você quiser baixar o código e trabalhar localmente ou em outro editor on-line, basta criar os três arquivos mencionados acima no mesmo diretório e copiar e colar o código do modelo do Glitch em cada um deles.
6. Boilerplate HTML do app
Por onde começo?
Todos os protótipos exigem uma estrutura HTML básica em que você pode renderizar suas descobertas. Faça isso agora. Você vai adicionar:
- Um título para a página.
- Algum texto descritivo.
- Um parágrafo de status.
- Um vídeo para manter o feed da webcam quando estiver pronto.
- Vários botões para iniciar a câmera, coletar dados ou redefinir a experiência.
- Importações para o TensorFlow.js e arquivos JS que você vai programar mais tarde.
Abra index.html e cole o código abaixo para configurar os recursos acima:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Transfer Learning - TensorFlow.js</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Import the webpage's stylesheet -->
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>Make your own "Teachable Machine" using Transfer Learning with MobileNet v3 in TensorFlow.js using saved graph model from TFHub.</h1>
<p id="status">Awaiting TF.js load</p>
<video id="webcam" autoplay muted></video>
<button id="enableCam">Enable Webcam</button>
<button class="dataCollector" data-1hot="0" data-name="Class 1">Gather Class 1 Data</button>
<button class="dataCollector" data-1hot="1" data-name="Class 2">Gather Class 2 Data</button>
<button id="train">Train & Predict!</button>
<button id="reset">Reset</button>
<!-- Import TensorFlow.js library -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.11.0/dist/tf.min.js" type="text/javascript"></script>
<!-- Import the page's JavaScript to do some stuff -->
<script type="module" src="/script.js"></script>
</body>
</html>
Cai na roda
Vamos analisar parte do código HTML acima para destacar alguns elementos importantes que você adicionou.
- Você adicionou uma tag
<h1>para o título da página e uma tag<p>com o ID "status", que é onde as informações serão impressas à medida que você usa diferentes partes do sistema para visualizar as saídas. - Você adicionou um elemento
<video>com o ID "webcam", em que vai renderizar a transmissão da webcam mais tarde. - Você adicionou cinco elementos
<button>. O primeiro, com o ID "enableCam", ativa a câmera. Os dois botões seguintes têm uma classe "dataCollector", que permite coletar imagens de exemplo para os objetos que você quer reconhecer. O código que você escrever mais tarde será projetado para que seja possível adicionar qualquer número desses botões, e eles vão funcionar automaticamente como esperado.
Esses botões também têm um atributo especial definido pelo usuário chamado "data-1hot", com um valor inteiro começando em 0 para a primeira classe. Esse é o índice numérico que você vai usar para representar os dados de uma determinada classe. O índice será usado para codificar as classes de saída corretamente com uma representação numérica em vez de uma string, já que os modelos de ML só funcionam com números.
Há também um atributo "data-name" que contém o nome legível que você quer usar para essa classe. Assim, é possível fornecer um nome mais significativo ao usuário em vez de um valor de índice numérico da codificação one-hot.
Por fim, você tem um botão de treinamento e redefinição para iniciar o processo de treinamento depois que os dados forem coletados ou para redefinir o app, respectivamente.
- Você também adicionou duas importações de
<script>. Um para o TensorFlow.js e outro para o script.js, que você vai definir em breve.
7. Adic. estilo
Padrões de elementos
Adicione estilos aos elementos HTML que você acabou de adicionar para garantir que eles sejam renderizados corretamente. Confira alguns estilos adicionados para posicionar e dimensionar elementos corretamente. Nada demais. Você pode adicionar mais tarde para criar uma UX ainda melhor, como você viu no vídeo da máquina ensinável.
style.css
body {
font-family: helvetica, arial, sans-serif;
margin: 2em;
}
h1 {
font-style: italic;
color: #FF6F00;
}
video {
clear: both;
display: block;
margin: 10px;
background: #000000;
width: 640px;
height: 480px;
}
button {
padding: 10px;
float: left;
margin: 5px 3px 5px 10px;
}
.removed {
display: none;
}
#status {
font-size:150%;
}
Ótimo! Isso é tudo o que você precisa. Se você visualizar a saída agora, ela será parecida com esta:

8. JavaScript: constantes e listeners de teclas
Definir constantes principais
Primeiro, adicione algumas constantes importantes que você vai usar em todo o app. Comece substituindo o conteúdo de script.js por estas constantes:
script.js
const STATUS = document.getElementById('status');
const VIDEO = document.getElementById('webcam');
const ENABLE_CAM_BUTTON = document.getElementById('enableCam');
const RESET_BUTTON = document.getElementById('reset');
const TRAIN_BUTTON = document.getElementById('train');
const MOBILE_NET_INPUT_WIDTH = 224;
const MOBILE_NET_INPUT_HEIGHT = 224;
const STOP_DATA_GATHER = -1;
const CLASS_NAMES = [];
Vamos detalhar para que servem:
STATUSsimplesmente contém uma referência à tag de parágrafo em que você vai escrever as atualizações de status.VIDEOcontém uma referência ao elemento de vídeo HTML que vai renderizar a transmissão da webcam.ENABLE_CAM_BUTTON,RESET_BUTTONeTRAIN_BUTTONcapturam referências DOM para todos os botões principais da página HTML.MOBILE_NET_INPUT_WIDTHeMOBILE_NET_INPUT_HEIGHTdefinem a largura e a altura esperadas da entrada do modelo MobileNet, respectivamente. Ao armazenar isso em uma constante perto da parte de cima do arquivo, se você decidir usar uma versão diferente mais tarde, será mais fácil atualizar os valores uma vez em vez de ter que substituir em vários lugares diferentes.STOP_DATA_GATHERé definido como -1. Isso armazena um valor de estado para que você saiba quando o usuário parou de clicar em um botão para coletar dados do feed da webcam. Ao dar um nome mais significativo a esse número, o código fica mais legível depois.CLASS_NAMESfunciona como uma pesquisa e contém os nomes legíveis das possíveis previsões de classe. Essa matriz será preenchida mais tarde.
Agora que você tem referências aos elementos principais, é hora de associar alguns listeners de eventos a eles.
Adicionar listeners de eventos principais
Comece adicionando manipuladores de eventos de clique aos botões principais, como mostrado abaixo:
script.js
ENABLE_CAM_BUTTON.addEventListener('click', enableCam);
TRAIN_BUTTON.addEventListener('click', trainAndPredict);
RESET_BUTTON.addEventListener('click', reset);
function enableCam() {
// TODO: Fill this out later in the codelab!
}
function trainAndPredict() {
// TODO: Fill this out later in the codelab!
}
function reset() {
// TODO: Fill this out later in the codelab!
}
ENABLE_CAM_BUTTON: chama a função "enableCam" quando clicado.
TRAIN_BUTTON: chama trainAndPredict quando clicado.
RESET_BUTTON: chama a redefinição quando clicado.
Por fim, nesta seção, você pode encontrar todos os botões que têm uma classe "dataCollector" usando document.querySelectorAll(). Isso retorna uma matriz de elementos encontrados no documento que correspondem a:
script.js
let dataCollectorButtons = document.querySelectorAll('button.dataCollector');
for (let i = 0; i < dataCollectorButtons.length; i++) {
dataCollectorButtons[i].addEventListener('mousedown', gatherDataForClass);
dataCollectorButtons[i].addEventListener('mouseup', gatherDataForClass);
// Populate the human readable names for classes.
CLASS_NAMES.push(dataCollectorButtons[i].getAttribute('data-name'));
}
function gatherDataForClass() {
// TODO: Fill this out later in the codelab!
}
Explicação do código:
Em seguida, percorra os botões encontrados e associe dois listeners de eventos a cada um. Um para "mousedown" e outro para "mouseup". Isso permite que você continue gravando amostras enquanto o botão estiver pressionado, o que é útil para a coleta de dados.
Os dois eventos chamam uma função gatherDataForClass que você vai definir mais tarde.
Neste ponto, também é possível enviar os nomes de classe legíveis encontrados do atributo data-name do botão HTML para a matriz CLASS_NAMES.
Em seguida, adicione algumas variáveis para armazenar elementos importantes que serão usados mais tarde.
script.js
let mobilenet = undefined;
let gatherDataState = STOP_DATA_GATHER;
let videoPlaying = false;
let trainingDataInputs = [];
let trainingDataOutputs = [];
let examplesCount = [];
let predict = false;
Vamos conferir cada uma delas.
Primeiro, você tem uma variável mobilenet para armazenar o modelo mobilenet carregado. Inicialmente, defina como indefinido.
Em seguida, você tem uma variável chamada gatherDataState. Se um botão "dataCollector" for pressionado, isso vai mudar para o ID de 1 hot do botão, conforme definido no HTML. Assim, você sabe qual classe de dados está coletando naquele momento. Inicialmente, esse valor é definido como STOP_DATA_GATHER para que o loop de coleta de dados que você escrever mais tarde não colete dados quando nenhum botão estiver sendo pressionado.
O videoPlaying acompanha se o stream da webcam foi carregado e está sendo reproduzido corretamente e se está disponível para uso. Inicialmente, essa opção é definida como false porque a webcam só é ativada quando você pressiona o botão ENABLE_CAM_BUTTON..
Em seguida, defina duas matrizes, trainingDataInputs e trainingDataOutputs. Eles armazenam os valores dos dados de treinamento coletados à medida que você clica nos botões "dataCollector" para os recursos de entrada gerados pelo modelo de base MobileNet e a classe de saída amostrada, respectivamente.
Uma matriz final, examplesCount,, é definida para acompanhar quantos exemplos estão contidos em cada classe depois que você começa a adicioná-los.
Por fim, você tem uma variável chamada predict que controla seu loop de previsão. Ele é definido como false inicialmente. Nenhuma previsão poderá ser feita até que isso seja definido como true mais tarde.
Agora que todas as variáveis principais foram definidas, vamos carregar o modelo de base MobileNet v3 pré-cortado que fornece vetores de atributos de imagem em vez de classificações.
9. Carregar o modelo base do MobileNet
Primeiro, defina uma nova função chamada loadMobileNetFeatureModel, conforme mostrado abaixo. Ela precisa ser uma função assíncrona, já que o ato de carregar um modelo é assíncrono:
script.js
/**
* Loads the MobileNet model and warms it up so ready for use.
**/
async function loadMobileNetFeatureModel() {
const URL =
'https://tfhub.dev/google/tfjs-model/imagenet/mobilenet_v3_small_100_224/feature_vector/5/default/1';
mobilenet = await tf.loadGraphModel(URL, {fromTFHub: true});
STATUS.innerText = 'MobileNet v3 loaded successfully!';
// Warm up the model by passing zeros through it once.
tf.tidy(function () {
let answer = mobilenet.predict(tf.zeros([1, MOBILE_NET_INPUT_HEIGHT, MOBILE_NET_INPUT_WIDTH, 3]));
console.log(answer.shape);
});
}
// Call the function immediately to start loading.
loadMobileNetFeatureModel();
Neste código, você define o URL em que o modelo a ser carregado está localizado na documentação do TFHub.
Em seguida, carregue o modelo usando await tf.loadGraphModel(). Não se esqueça de definir a propriedade especial fromTFHub como true ao carregar um modelo deste site do Google. Esse é um caso especial apenas para usar modelos hospedados no TF Hub, em que essa propriedade extra precisa ser definida.
Quando o carregamento for concluído, defina o innerText do elemento STATUS com uma mensagem para que você possa ver visualmente que ele foi carregado corretamente e que está tudo pronto para começar a coletar dados.
Agora, só falta aquecer o modelo. Com modelos maiores como esse, a primeira vez que você usa o modelo, pode levar um tempo para configurar tudo. Por isso, é útil transmitir zeros pelo modelo para evitar qualquer espera no futuro, quando o tempo pode ser mais crítico.
Você pode usar tf.zeros() envolvido em um tf.tidy() para garantir que os tensores sejam descartados corretamente, com um tamanho de lote de 1 e a altura e largura corretas definidas nas constantes no início. Por fim, especifique os canais de cores, que, nesse caso, são três, já que o modelo espera imagens RGB.
Em seguida, registre a forma resultante do tensor retornado usando answer.shape() para ajudar você a entender o tamanho dos recursos de imagem que esse modelo produz.
Depois de definir essa função, você pode chamá-la imediatamente para iniciar o download do modelo no carregamento de página.
Se você visualizar a prévia dinâmica agora, depois de alguns momentos, o texto de status vai mudar de "Aguardando o carregamento do TF.js" para "MobileNet v3 carregado com sucesso!", conforme mostrado abaixo. Confira se isso funciona antes de continuar.

Você também pode verificar a saída do console para ver o tamanho impresso dos recursos de saída produzidos por esse modelo. Depois de executar zeros no modelo MobileNet, você vai ver uma forma de [1, 1024] impressa. O primeiro item é apenas o tamanho do lote de 1, e você pode ver que ele retorna 1.024 recursos que podem ser usados para ajudar a classificar novos objetos.
10. Definir o novo cabeçalho do modelo
Agora é hora de definir o cabeçalho do modelo, que é essencialmente um perceptron multicamadas muito minimalista.
script.js
let model = tf.sequential();
model.add(tf.layers.dense({inputShape: [1024], units: 128, activation: 'relu'}));
model.add(tf.layers.dense({units: CLASS_NAMES.length, activation: 'softmax'}));
model.summary();
// Compile the model with the defined optimizer and specify a loss function to use.
model.compile({
// Adam changes the learning rate over time which is useful.
optimizer: 'adam',
// Use the correct loss function. If 2 classes of data, must use binaryCrossentropy.
// Else categoricalCrossentropy is used if more than 2 classes.
loss: (CLASS_NAMES.length === 2) ? 'binaryCrossentropy': 'categoricalCrossentropy',
// As this is a classification problem you can record accuracy in the logs too!
metrics: ['accuracy']
});
Vamos analisar esse código. Comece definindo um modelo tf.sequential ao qual você vai adicionar camadas de modelo.
Em seguida, adicione uma camada densa como a camada de entrada desse modelo. Ele tem uma forma de entrada de 1024, já que as saídas dos recursos do MobileNet v3 têm esse tamanho. Você descobriu isso na etapa anterior depois de transmitir uns pelo modelo. Essa camada tem 128 neurônios que usam a função de ativação ReLU.
Se você não conhece as funções de ativação e as camadas do modelo, faça o curso detalhado no início deste workshop para entender o que essas propriedades fazem nos bastidores.
A próxima camada a ser adicionada é a camada final. O número de neurônios precisa ser igual ao número de classes que você está tentando prever. Para isso, use CLASS_NAMES.length e descubra quantas classes você planeja classificar, o que é igual ao número de botões de coleta de dados encontrados na interface do usuário. Como esse é um problema de classificação, você usa a ativação softmax nessa camada final, que precisa ser usada ao tentar criar um modelo para resolver problemas de classificação em vez de regressão.
Agora, imprima um model.summary() para mostrar a visão geral do modelo recém-definido no console.
Por fim, compile o modelo para que ele esteja pronto para ser treinado. Aqui, o otimizador está definido como adam, e a perda será binaryCrossentropy se CLASS_NAMES.length for igual a 2 ou usará categoricalCrossentropy se houver três ou mais classes para classificar. As métricas de acurácia também são solicitadas para que possam ser monitoradas nos registros posteriormente para fins de depuração.
No console, você vai ver algo parecido com isto:

Ele tem mais de 130 mil parâmetros treináveis. Mas, como essa é uma camada densa simples de neurônios regulares, ela será treinada bem rápido.
Como uma atividade a ser feita depois que o projeto for concluído, tente mudar o número de neurônios na primeira camada para ver o quão baixo é possível chegar sem perder um desempenho razoável. Muitas vezes, o aprendizado de máquina envolve um nível de tentativa e erro para encontrar os valores de parâmetro ideais e oferecer a melhor troca entre uso de recursos e velocidade.
11. Ativar a webcam
Agora é hora de desenvolver a função enableCam() que você definiu antes. Adicione uma nova função chamada hasGetUserMedia(), conforme mostrado abaixo, e substitua o conteúdo da função enableCam() definida anteriormente pelo código correspondente abaixo.
script.js
function hasGetUserMedia() {
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
}
function enableCam() {
if (hasGetUserMedia()) {
// getUsermedia parameters.
const constraints = {
video: true,
width: 640,
height: 480
};
// Activate the webcam stream.
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
VIDEO.srcObject = stream;
VIDEO.addEventListener('loadeddata', function() {
videoPlaying = true;
ENABLE_CAM_BUTTON.classList.add('removed');
});
});
} else {
console.warn('getUserMedia() is not supported by your browser');
}
}
Primeiro, crie uma função chamada hasGetUserMedia() para verificar se o navegador é compatível com getUserMedia(). Para isso, verifique a existência de propriedades importantes das APIs do navegador.
Na função enableCam(), use a função hasGetUserMedia() que você acabou de definir acima para verificar se ela é compatível. Se não for, imprima um aviso no console.
Se ele for compatível, defina algumas restrições para sua chamada getUserMedia(), como querer apenas o fluxo de vídeo e preferir que o width do vídeo tenha 640 pixels de tamanho e o height tenha 480 pixels. Por quê? Não faz muito sentido ter um vídeo maior do que isso, já que ele precisaria ser redimensionado para 224 por 224 pixels para ser usado no modelo MobileNet. Você também pode economizar recursos de computação pedindo uma resolução menor. A maioria das câmeras é compatível com uma resolução desse tamanho.
Em seguida, chame navigator.mediaDevices.getUserMedia() com o constraints detalhado acima e aguarde o retorno do stream. Depois que o stream for retornado, você poderá acessar o elemento VIDEO para reproduzir o stream definindo-o como o valor srcObject.
Você também precisa adicionar um eventListener ao elemento VIDEO para saber quando o stream foi carregado e está sendo reproduzido.
Depois que o fluxo for carregado, defina videoPlaying como "true" e remova ENABLE_CAM_BUTTON para evitar que ele seja clicado novamente definindo a classe como "removed".
Agora execute o código, clique no botão para ativar a câmera e permita o acesso à webcam. Se esta for a primeira vez que você faz isso, sua imagem vai aparecer no elemento de vídeo na página, como mostrado abaixo:

Agora é hora de adicionar uma função para lidar com os cliques no botão dataCollector.
12. Manipulador de eventos do botão de coleta de dados
Agora é hora de preencher a função atualmente vazia chamada gatherDataForClass().. Essa é a função que você atribuiu como manipulador de eventos para os botões dataCollector no início do codelab.
script.js
/**
* Handle Data Gather for button mouseup/mousedown.
**/
function gatherDataForClass() {
let classNumber = parseInt(this.getAttribute('data-1hot'));
gatherDataState = (gatherDataState === STOP_DATA_GATHER) ? classNumber : STOP_DATA_GATHER;
dataGatherLoop();
}
Primeiro, verifique o atributo data-1hot no botão clicado no momento chamando this.getAttribute() com o nome do atributo, neste caso data-1hot como parâmetro. Como é uma string, você pode usar parseInt() para transmitir como um número inteiro e atribuir esse resultado a uma variável chamada classNumber..
Em seguida, defina a variável gatherDataState de acordo com a situação. Se o gatherDataState atual for igual a STOP_DATA_GATHER (que você definiu como -1), isso significa que você não está coletando dados no momento e que um evento mousedown foi acionado. Defina o gatherDataState como o classNumber que você acabou de encontrar.
Caso contrário, significa que você está coletando dados e o evento disparado foi um evento mouseup, e agora você quer interromper a coleta de dados para essa classe. Basta definir o estado STOP_DATA_GATHER para encerrar o loop de coleta de dados que você vai definir em breve.
Por fim, inicie a chamada para dataGatherLoop(),, que realiza a gravação dos dados da classe.
13. Coleta de dados
Agora, defina a função dataGatherLoop(). Essa função é responsável por amostrar imagens do vídeo da webcam, transmiti-las pelo modelo MobileNet e capturar as saídas desse modelo (os 1.024 vetores de atributos).
Em seguida, ele os armazena junto com o ID gatherDataState do botão que está sendo pressionado para que você saiba qual classe esses dados representam.
Veja como fazer isso:
script.js
function dataGatherLoop() {
if (videoPlaying && gatherDataState !== STOP_DATA_GATHER) {
let imageFeatures = tf.tidy(function() {
let videoFrameAsTensor = tf.browser.fromPixels(VIDEO);
let resizedTensorFrame = tf.image.resizeBilinear(videoFrameAsTensor, [MOBILE_NET_INPUT_HEIGHT,
MOBILE_NET_INPUT_WIDTH], true);
let normalizedTensorFrame = resizedTensorFrame.div(255);
return mobilenet.predict(normalizedTensorFrame.expandDims()).squeeze();
});
trainingDataInputs.push(imageFeatures);
trainingDataOutputs.push(gatherDataState);
// Intialize array index element if currently undefined.
if (examplesCount[gatherDataState] === undefined) {
examplesCount[gatherDataState] = 0;
}
examplesCount[gatherDataState]++;
STATUS.innerText = '';
for (let n< = 0; n CLASS_NAMES.length; n++) {
STATUS.innerText += CLASS_NAMES[n] + ' data count: ' + examplesCount[n] + '. ';
}
window.requestAnimationFrame(dataGatherLoop);
}
}
Você só vai continuar a execução dessa função se videoPlaying for verdadeiro, ou seja, se a webcam estiver ativa, e gatherDataState não for igual a STOP_DATA_GATHER e um botão de coleta de dados da turma estiver sendo pressionado.
Em seguida, encapsule o código em um tf.tidy() para descartar os tensores criados no código a seguir. O resultado dessa execução de código tf.tidy() é armazenado em uma variável chamada imageFeatures.
Agora é possível capturar um frame da webcam VIDEO usando tf.browser.fromPixels(). O tensor resultante que contém os dados da imagem é armazenado em uma variável chamada videoFrameAsTensor.
Em seguida, redimensione a variável videoFrameAsTensor para que ela tenha o formato correto para a entrada do modelo MobileNet. Use uma chamada tf.image.resizeBilinear() com o tensor que você quer redimensionar como o primeiro parâmetro e uma forma que defina a nova altura e largura, conforme definido pelas constantes que você criou anteriormente. Por fim, defina "align corners" como "true" transmitindo o terceiro parâmetro para evitar problemas de alinhamento ao redimensionar. O resultado desse redimensionamento é armazenado em uma variável chamada resizedTensorFrame.
Esse redimensionamento primitivo estica a imagem, já que a imagem da webcam tem 640 por 480 pixels, e o modelo precisa de uma imagem quadrada de 224 por 224 pixels.
Para os fins desta demonstração, isso deve funcionar bem. No entanto, depois de concluir este codelab, talvez você queira tentar cortar um quadrado dessa imagem para ter resultados ainda melhores em qualquer sistema de produção que criar depois.
Em seguida, normalize os dados da imagem. Os dados de imagem estão sempre no intervalo de 0 a 255 ao usar tf.browser.frompixels(). Portanto, basta dividir resizedTensorFrame por 255 para garantir que todos os valores estejam entre 0 e 1, que é o que o modelo MobileNet espera como entradas.
Por fim, na seção tf.tidy() do código, transmita esse tensor normalizado pelo modelo carregado chamando mobilenet.predict(), a que você passa a versão expandida de normalizedTensorFrame usando expandDims() para que seja um lote de 1, já que o modelo espera um lote de entradas para processamento.
Quando o resultado voltar, você poderá chamar imediatamente squeeze() nele para reduzir a um tensor 1D, que você vai retornar e atribuir à variável imageFeatures que captura o resultado de tf.tidy().
Agora que você tem o imageFeatures do modelo MobileNet, é possível gravar esses valores enviando-os para a matriz trainingDataInputs que você definiu anteriormente.
Você também pode gravar o que essa entrada representa enviando o gatherDataState atual para a matriz trainingDataOutputs.
A variável gatherDataState teria sido definida como o ID numérico da classe atual para a qual você está registrando dados quando o botão foi clicado na função gatherDataForClass() definida anteriormente.
Neste ponto, você também pode aumentar o número de exemplos de uma determinada classe. Para isso, primeiro verifique se o índice na matriz examplesCount foi inicializado antes ou não. Se ele não estiver definido, defina como 0 para inicializar o contador do ID numérico de uma determinada classe. Depois, incremente o examplesCount do gatherDataState atual.
Agora, atualize o texto do elemento STATUS na página da Web para mostrar as contagens atuais de cada classe à medida que são capturadas. Para fazer isso, faça um loop na matriz CLASS_NAMES e imprima o nome legível por humanos combinado com a contagem de dados no mesmo índice em examplesCount.
Por fim, chame window.requestAnimationFrame() com dataGatherLoop transmitido como um parâmetro para chamar essa função de novo de forma recursiva. Isso vai continuar a amostrar frames do vídeo até que o mouseup do botão seja detectado e gatherDataState seja definido como STOP_DATA_GATHER,. Nesse momento, o loop de coleta de dados será encerrado.
Se você executar o código agora, poderá clicar no botão "Ativar câmera", aguardar o carregamento da webcam e clicar e manter pressionado cada um dos botões de coleta de dados para reunir exemplos de cada classe de dados. Aqui você vê como eu coletei dados para meu smartphone e minha mão, respectivamente.

O texto de status será atualizado à medida que armazena todos os tensores na memória, conforme mostrado na captura de tela acima.
14. Treinar e prever
A próxima etapa é implementar o código para a função trainAndPredict(), que está vazia no momento e é onde ocorre o aprendizado por transferência. Vamos analisar o código:
script.js
async function trainAndPredict() {
predict = false;
tf.util.shuffleCombo(trainingDataInputs, trainingDataOutputs);
let outputsAsTensor = tf.tensor1d(trainingDataOutputs, 'int32');
let oneHotOutputs = tf.oneHot(outputsAsTensor, CLASS_NAMES.length);
let inputsAsTensor = tf.stack(trainingDataInputs);
let results = await model.fit(inputsAsTensor, oneHotOutputs, {shuffle: true, batchSize: 5, epochs: 10,
callbacks: {onEpochEnd: logProgress} });
outputsAsTensor.dispose();
oneHotOutputs.dispose();
inputsAsTensor.dispose();
predict = true;
predictLoop();
}
function logProgress(epoch, logs) {
console.log('Data for epoch ' + epoch, logs);
}
Primeiro, defina predict como false para interromper as previsões atuais.
Em seguida, embaralhe as matrizes de entrada e saída usando tf.util.shuffleCombo() para garantir que a ordem não cause problemas no treinamento.
Converta a matriz de saída, trainingDataOutputs,, para um tensor1d do tipo int32 para que ela possa ser usada em uma codificação one-hot. Isso é armazenado em uma variável chamada outputsAsTensor.
Use a função tf.oneHot() com essa variável outputsAsTensor e o número máximo de classes a serem codificadas, que é apenas CLASS_NAMES.length. Suas saídas codificadas com um número 1 agora estão armazenadas em um novo tensor chamado oneHotOutputs.
No momento, trainingDataInputs é uma matriz de tensores gravados. Para usar esses dados no treinamento, é necessário converter a matriz de tensores em um tensor 2D regular.
Para isso, há uma ótima função na biblioteca do TensorFlow.js chamada tf.stack().
que recebe uma matriz de tensores e os empilha para produzir um tensor de dimensão maior como saída. Nesse caso, um tensor 2D é retornado, que é um lote de entradas unidimensionais de 1024 de comprimento cada, contendo os recursos registrados, que é o que você precisa para o treinamento.
Em seguida, await model.fit() para treinar o cabeçalho do modelo personalizado. Aqui, você transmite sua variável inputsAsTensor junto com o oneHotOutputs para representar os dados de treinamento a serem usados como entradas e saídas de destino, respectivamente. No objeto de configuração do terceiro parâmetro, defina shuffle como true, use batchSize de 5, com epochs definido como 10 e especifique um callback para onEpochEnd na função logProgress que você vai definir em breve.
Por fim, descarte os tensores criados, já que o modelo foi treinado. Em seguida, defina predict de volta como true para permitir que as previsões aconteçam novamente e chame a função predictLoop() para começar a prever imagens da webcam em tempo real.
Você também pode definir a função logProcess() para registrar o estado do treinamento, que é usado em model.fit() acima e imprime os resultados no console após cada rodada de treinamento.
Falta pouco. Agora é hora de adicionar a função predictLoop() para fazer previsões.
Loop de previsão principal
Aqui você implementa o loop de previsão principal que faz amostragem de frames de uma webcam e prevê continuamente o que está em cada frame com resultados em tempo real no navegador.
Vamos conferir o código:
script.js
function predictLoop() {
if (predict) {
tf.tidy(function() {
let videoFrameAsTensor = tf.browser.fromPixels(VIDEO).div(255);
let resizedTensorFrame = tf.image.resizeBilinear(videoFrameAsTensor,[MOBILE_NET_INPUT_HEIGHT,
MOBILE_NET_INPUT_WIDTH], true);
let imageFeatures = mobilenet.predict(resizedTensorFrame.expandDims());
let prediction = model.predict(imageFeatures).squeeze();
let highestIndex = prediction.argMax().arraySync();
let predictionArray = prediction.arraySync();
STATUS.innerText = 'Prediction: ' + CLASS_NAMES[highestIndex] + ' with ' + Math.floor(predictionArray[highestIndex] * 100) + '% confidence';
});
window.requestAnimationFrame(predictLoop);
}
}
Primeiro, verifique se predict é verdadeiro para que as previsões só sejam feitas depois que um modelo for treinado e estiver disponível para uso.
Em seguida, você pode extrair os recursos da imagem atual da mesma forma que fez na função dataGatherLoop(). Basicamente, você pega um frame da webcam usando tf.browser.from pixels(), normaliza e redimensiona para 224 por 224 pixels. Em seguida, transmite esses dados pelo modelo MobileNet para receber os recursos de imagem resultantes.
Agora, no entanto, você pode usar a nova cabeça de modelo treinada para fazer uma previsão transmitindo o imageFeatures resultante que acabou de ser encontrado pela função predict() do modelo treinado. Em seguida, você pode compactar o tensor resultante para torná-lo unidimensional novamente e atribuí-lo a uma variável chamada prediction.
Com esse prediction, é possível encontrar o índice com o maior valor usando argMax() e converter o tensor resultante em uma matriz usando arraySync() para acessar os dados subjacentes em JavaScript e descobrir a posição do elemento de maior valor. Esse valor é armazenado na variável chamada highestIndex.
Você também pode receber os níveis de confiança da previsão da mesma forma chamando arraySync() diretamente no tensor prediction.
Agora você tem tudo o que precisa para atualizar o texto STATUS com os dados prediction. Para receber a string legível da classe, basta pesquisar o highestIndex na matriz CLASS_NAMES e extrair o valor de confiança do predictionArray. Para facilitar a leitura como uma porcentagem, basta multiplicar por 100 e math.floor() o resultado.
Por fim, você pode usar window.requestAnimationFrame() para chamar predictionLoop() novamente quando estiver tudo pronto e receber a classificação em tempo real do seu stream de vídeo. Isso continua até que predict seja definido como false se você escolher treinar um novo modelo com novos dados.
O que nos leva à peça final do quebra-cabeça. Implementar o botão de redefinição.
15. Implementar o botão de redefinição
Quase pronto! A peça final do quebra-cabeça é implementar um botão de redefinição para começar de novo. Confira abaixo o código da sua função reset(), que está vazia no momento. Atualize da seguinte forma:
script.js
/**
* Purge data and start over. Note this does not dispose of the loaded
* MobileNet model and MLP head tensors as you will need to reuse
* them to train a new model.
**/
function reset() {
predict = false;
examplesCount.length = 0;
for (let i = 0; i < trainingDataInputs.length; i++) {
trainingDataInputs[i].dispose();
}
trainingDataInputs.length = 0;
trainingDataOutputs.length = 0;
STATUS.innerText = 'No data collected';
console.log('Tensors in memory: ' + tf.memory().numTensors);
}
Primeiro, interrompa todos os loops de previsão em execução definindo predict como false. Em seguida, exclua todo o conteúdo da matriz examplesCount definindo o comprimento dela como 0, o que é uma maneira prática de limpar todo o conteúdo de uma matriz.
Agora, analise todos os trainingDataInputs gravados e dispose() cada tensor contido nele para liberar memória novamente, já que os tensores não são limpos pelo coletor de lixo do JavaScript.
Depois disso, você pode definir com segurança o comprimento da matriz como 0 nas matrizes trainingDataInputs e trainingDataOutputs para limpar também.
Por fim, defina o texto STATUS como algo sensato e imprima os tensores restantes na memória como uma verificação de integridade.
Observe que ainda haverá algumas centenas de tensores na memória, já que o modelo MobileNet e o perceptron multicamadas que você definiu não são descartados. Você vai precisar reutilizá-los com novos dados de treinamento se decidir treinar novamente após essa redefinição.
16. Vamos fazer um teste!
É hora de testar sua própria versão do Teachable Machine!
Acesse a prévia ao vivo, ative a webcam, colete pelo menos 30 amostras para a classe 1 de algum objeto no ambiente e faça o mesmo para a classe 2 de um objeto diferente. Clique em "Treinar" e verifique o registro do console para acompanhar o progresso. O treinamento deve ser bem rápido:

Depois de treinado, mostre os objetos para a câmera e receba previsões em tempo real que serão impressas na área de texto de status na parte superior da página da Web. Se você estiver com problemas, confira meu código de trabalho concluído para ver se você esqueceu de copiar algo.
17. Parabéns
Parabéns! Você acabou de concluir seu primeiro exemplo de aprendizado por transferência usando o TensorFlow.js no navegador.
Teste em vários objetos. Você vai notar que algumas coisas são mais difíceis de reconhecer do que outras, principalmente se forem parecidas com algo mais. Talvez seja necessário adicionar mais classes ou dados de treinamento para diferenciá-las.
Resumo
Neste codelab, você aprendeu a:
- O que é aprendizado por transferência e quais são as vantagens dele em relação ao treinamento de um modelo completo.
- Como conseguir modelos para reutilização no TensorFlow Hub.
- Como configurar um app da Web adequado para aprendizado por transferência.
- Como carregar e usar um modelo de base para gerar recursos de imagem.
- Como treinar um novo cabeçalho de previsão que pode reconhecer objetos personalizados em imagens de webcam.
- Como usar os modelos resultantes para classificar dados em tempo real.
Qual é a próxima etapa?
Agora que você já tem uma base de trabalho para começar, quais ideias criativas você pode adotar para ampliar esse modelo de implantação de modelo de machine learning para um caso de uso real em que você esteja trabalhando? Talvez você possa revolucionar o setor em que trabalha atualmente para ajudar as pessoas da sua empresa a treinar modelos para classificar coisas importantes no trabalho diário. As possibilidades são infinitas.
Para ir mais longe, faça este curso completo sem custo financeiro, que mostra como combinar os dois modelos que você tem neste codelab em um único modelo para aumentar a eficiência.
Se quiser saber mais sobre a teoria por trás do aplicativo original da Máquina que Aprende, confira este tutorial.
Compartilhe o que você sabe conosco
Você pode estender facilmente o que fez hoje para outros casos de uso criativos. Recomendamos que você pense fora da caixa e continue criando.
Marque-nos nas redes sociais usando a hashtag #MadeWithTFJS para que seu projeto tenha a chance de ser destacado no nosso blog do TensorFlow ou até em eventos futuros. Adoraríamos ver o que você cria.
Sites para conferir
- Site oficial do TensorFlow.js
- Modelos prontos do TensorFlow.js
- API do TensorFlow.js
- Show & Tell do TensorFlow.js: inspire-se e veja o que outras pessoas criaram.