TensorFlow.js: crie sua própria "Máquina que Aprende" usando aprendizado por transferência com o TensorFlow.js

1. Antes de começar

O uso do modelo TensorFlow.js cresce exponencialmente nos últimos anos, e muitos desenvolvedores de JavaScript estão procurando usar modelos de última geração para retreiná-los e trabalhar com dados personalizados exclusivos a indústria. Usar um modelo existente (geralmente chamado de modelo base) e usá-lo 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 um modelo completamente em branco. É possível reutilizar o conhecimento já aprendido com um modelo treinado anteriormente e é necessário ter menos exemplos do novo item que você quer classificar. Além disso, o treinamento costuma ser significativamente mais rápido porque é preciso treinar novamente as poucas camadas da arquitetura do modelo em vez de toda a rede. Por esse motivo, 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 fácil aquisição de dados.

Este codelab mostra como criar um app da Web em uma tela em branco para recriar o conhecido site Teachable Machine do Google. O site permite que você crie um app da Web funcional que qualquer usuário possa usar para reconhecer um objeto personalizado com apenas alguns exemplos de imagens da webcam. O site é propositalmente mínimo para que você possa se concentrar nos aspectos de machine learning deste codelab. No entanto, assim como o site original da Máquina que Aprende, há muito escopo para aplicar sua experiência atual de desenvolvedor da Web para melhorar a UX.

Pré-requisitos

Este codelab foi desenvolvido para desenvolvedores da Web que estão familiarizados com os modelos predefinidos do TensorFlow.js e com o uso básico da API e querem começar a usar o aprendizado por transferência no TensorFlow.js.

  • Para fazer este laboratório, é preciso ter familiaridade com o TensorFlow.js, HTML5, CSS e JavaScript.

Caso você seja iniciante no Tensorflow.js,faça este curso gratuito de Zero para os heróis A primeira é que não pressupõe nenhum conhecimento sobre machine learning ou TensorFlow.js e ensina tudo o que você precisa saber em etapas menores.

O que você aprenderá

  • O que é o TensorFlow.js e por que usá-lo no seu próximo app da Web.
  • Como criar uma página da Web HTML/CSS /JS simplificada que reproduza a experiência do usuário do Máquina que Aprende.
  • Como usar o TensorFlow.js para carregar um modelo 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 periférico multicamadas que usa os recursos de imagem e aprende a classificar novos objetos.

Vamos hackear...

Pré-requisitos

  • Recomendamos que você tenha uma conta no Glitch.com ou use um ambiente de veiculação na Web com os quais se sinta confortável para editar e executar por conta própria.

2. O que é o TensorFlow.js?

54e81d02971f53e8.png

O TensorFlow.js é uma biblioteca de machine learning de código aberto que pode ser executada em qualquer lugar com JavaScript. Ele é baseado na biblioteca original do TensorFlow escrita em Python e tem como objetivo recriar essa experiência do desenvolvedor e um conjunto de APIs para o ecossistema JavaScript.

Onde ele pode ser usado?

Pela portabilidade do JavaScript, agora é possível escrever em uma linguagem e realizar machine learning em todas as plataformas a seguir com facilidade:

  • lado do cliente no navegador da Web usando JavaScript simples
  • Servidor e até dispositivos de IoT como o Raspberry Pi usando o Node.js
  • Apps para computador com o 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 que ele pode executar, como a CPU ou o 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 também manter as coisas funcionando rapidamente. Atualmente, o TensorFlow.js é compatível com o seguinte:

  • Execução do WebGL na placa de vídeo do dispositivo (GPU): esta é a maneira mais rápida de executar modelos maiores (com mais de 3 MB) com aceleração de GPU.
  • Execução do Web ssembly (WASM) na CPU: para melhorar o desempenho da CPU em dispositivos como os smartphones mais antigos, 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 à sobrecarga de fazer upload de conteúdo para um processador gráfico.
  • Execução da CPU: o substituto não deve estar disponível em nenhum dos outros ambientes. Esse é o caminho mais lento, mas está sempre disponível para você.

Observação:você poderá forçar um desses back-ends se souber em qual dispositivo será executado ou simplesmente deixar o TensorFlow.js decidir se. não especifique isso.

Superpoderes do lado do cliente

Executar o TensorFlow.js no navegador da Web na máquina cliente pode gerar muitos benefícios que vale a pena considerar.

Privacidade

É possível treinar e classificar dados na máquina cliente sem nunca enviá-los a um servidor da Web de terceiros. Às vezes, pode ser necessário obedecer à legislação local, 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 a câmera, o microfone, o GPS, o acelerômetro e muito mais, caso o usuário conceda acesso.

Alcance e escala

Com um clique, qualquer pessoa pode clicar em um link que você envia, abrir a página da Web no navegador e usar o que você criou. Você não precisa de uma configuração complexa para Linux no lado do servidor com drivers CUDA e muito mais, só para usar o sistema de machine learning.

Custo

Nenhum servidor significa que você só precisa pagar por uma CDN para hospedar seus arquivos HTML, CSS, JS e de modelo. O custo de uma CDN é muito mais barato do que manter um servidor (possivelmente com uma placa de vídeo anexada) funcionando 24 horas.

Recursos do lado do servidor

Ao usar a implementação do TensorFlow.js em Node.js, são permitidos os seguintes recursos.

Suporte completo ao CUDA

No lado do servidor, para aceleração da placa de vídeo, você precisa instalar os drivers da CUDA do NVIDIA para permitir que o TensorFlow funcione com a placa de vídeo, diferentemente do navegador que usa a WebGL. Não é necessário instalar nada. No entanto, com a compatibilidade total com a CUDA, é possível aproveitar ao máximo os recursos 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 ao da implementação do TensorFlow em Python, porque ambos compartilham o mesmo back-end C++.

Tamanho do modelo

Para modelos de pesquisa modernos, talvez você esteja trabalhando com modelos muito grandes, talvez gigabytes. No momento, não é possível executar esses modelos no navegador da Web devido às limitações do uso de memória por guia. Para executar esses modelos maiores, use o Node.js no seu próprio servidor com as especificações de hardware necessárias para executar esse modelo com eficiência.

IOL

O Node.js é compatível com computadores de placa única conhecidos, como o Raspberry Pi. 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 só funciona na compilação no tempo. Isso significa que você poderá notar ganhos de desempenho ao usar o Node.js, já que ele será otimizado no momento da execução, especialmente para qualquer pré-processamento realizado. Um ótimo exemplo disso pode ser visto neste estudo de caso, que mostra como a Hugging Face usou o Node.js para conseguir o dobro de desempenho no modelo de processamento de linguagem natural.

Agora que você já conhece os conceitos básicos do TensorFlow.js, onde ele pode ser executado e alguns dos benefícios, vamos começar a usar esse recurso.

3. Aprendizado por transferência

O que é exatamente o aprendizado por transferência?

O aprendizado por transferência envolve conhecimento que já foi aprendido para ajudar a 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 ser usadas para ajudar a reconhecer coisas novas que você nunca viu antes. Veja este salgueiro, por exemplo:

e28070392cd4afb9.png

Dependendo de onde você está no mundo, é possível que não tenha visto esse tipo de árvore antes.

Ainda assim, se eu perguntar se há salgueiros na nova imagem abaixo, vocês poderão vê-los bem rápido, mesmo que estejam em um ângulo diferente e um pouco diferente do original.

d9073a0d5df27222.png

Você já tem um monte de neurônios em seu cérebro, que sabem identificar objetos semelhantes a árvores, e outros neurônios que são bons em encontrar linhas retas longas. É possível reutilizar esse conhecimento para classificar rapidamente um salgueiro, que é um objeto semelhante a uma árvore com várias ramificações verticais retas.

Da mesma forma, se você tiver um modelo de machine learning que já foi treinado em um domínio, como reconhecimento de imagens, poderá reutilizá-lo para executar uma tarefa diferente, mas relacionada.

É possível fazer o mesmo com um modelo avançado como o MobileNet, que é um modelo de pesquisa muito conhecido, que executa o reconhecimento de imagem em 1.000 diferentes tipos de objetos. De cães a carros, ele foi treinado em um grande conjunto de dados conhecido como ImageNet com milhões de imagens rotuladas.

Nesta animação, é possível ver o grande número de camadas neste modelo do MobileNet V1:

7d4e1e35c1a89715.gif

Durante o treinamento, esse modelo aprendeu a extrair recursos comuns que são importantes para os mil objetos. Vários recursos de nível inferior usados para identificar esses objetos 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 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 de modelo típica de uma CNN que, neste caso, foi treinada para reconhecer dígitos escritos à mão de 0 a 9:

baf4e3d434576106.png

Se for possível separar as camadas de nível inferior pré-treinadas de um modelo treinado existente à esquerda, das camadas de classificação próximas ao final do modelo mostrado à direita (às vezes chamado de cabeçalho da classificação do modelo), você pode usar as camadas de nível inferior para produzir atributos de saída para qualquer imagem, com base nos dados originais em que foi treinado. Veja a mesma rede com o cabeçalho de classificação removido:

369a8a9041c6917d.png

Supondo que a nova coisa que você está tentando reconhecer também possa usar esses recursos de saída aprendidos pelo modelo anterior, há uma boa chance de que eles possam ser reutilizados para uma nova finalidade.

No diagrama acima, esse modelo hipotético foi treinado em dígitos. Portanto, talvez o que você aprendeu sobre dígitos também possa ser aplicado a letras como a, b e c.

Agora, podemos adicionar uma nova cabeça de classificação que tenta prever "a", "b" ou "c", como mostrado:

db97e5e60ae73bbd.png

Aqui, as camadas de nível inferior são congeladas e não são treinadas. Apenas o novo cabeçalho é atualizado para aprender com os recursos do modelo cortado pré-treinado à esquerda.

A prática de fazer isso é conhecida como aprendizado por transferência e é o que a máquina ensina nos bastidores.

Também é possível ver que, ao treinar apenas o periférico de várias camadas no final da rede, ele é treinado muito mais rápido do que se você tivesse que treinar toda a rede do zero.

Mas como é possível colocar as mãos em subpartes de um modelo? Acesse a próxima seção para descobrir.

4. TensorFlow Hub: modelos base

Encontrar um modelo base adequado para usar

Para modelos de pesquisa mais avançados e conhecidos, como o MobileNet, acesse o TensorFlow Hub e filtre por modelos adequados para o TensorFlow.js que usam a arquitetura MobileNet v3 para encontrar resultados como os mostrados aqui:

c5dc1420c6238c14.png

Alguns desses resultados são do tipo "classificação de imagem" (detalhado no canto superior esquerdo de cada resultado de card de modelo). Outros são do tipo "vetor de recurso de imagem".

Esses resultados de vetor de recurso de imagem são essencialmente as versões pré-cortadas do MobileNet que você pode usar para receber os vetores de recurso de imagem em vez da classificação final.

Modelos como esse costumam ser chamados de "modelos base", que você pode usar para realizar o aprendizado por transferência da mesma forma mostrada na seção anterior. Para isso, é preciso adicionar um novo cabeçalho de classificação e treiná-lo com seus próprios dados.

O próximo item a ser verificado é um modelo de base específico em que o formato do TensorFlow.js deve ser lançado. Se você abrir a página para um desses modelos de vetor de dispositivos móveis v3, verá na documentação do JS que eles estão na forma de um modelo de gráfico com base no snippet de código de exemplo na documentação que usa tf.loadGraphModel().

f97d903d2e46924b.png

Observe também que, se você encontrar um modelo no formato de camadas em vez de um formato de gráfico, poderá escolher quais serão congeladas e quais serão descongeladas para o treinamento. Isso pode ser muito útil ao criar um modelo para uma nova tarefa, geralmente chamada de "modelo de transferência". No entanto, por enquanto, você usará o tipo de modelo de gráfico padrão para este tutorial, no qual a maioria dos modelos do TF Hub é implantada. Para saber mais sobre como trabalhar com modelos de camadas, confira o curso zero to hero TensorFlow.js.

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 de modelo do zero?

Primeiro, o tempo de treinamento é uma vantagem importante de usar uma abordagem de aprendizado por transferência, porque você já tem um modelo base treinado como base.

Em segundo lugar, você pode evitar a exibição de muito menos exemplos do que você está tentando classificar devido ao treinamento que já aconteceu.

Isso é ótimo se você tem tempo e recursos limitados para coletar dados de exemplo do que quer classificar e precisa criar um protótipo rapidamente antes de coletar mais dados de treinamento para torná-lo mais robusto.

Considerando a necessidade de ter menos dados e a velocidade do treinamento de uma rede menor, o aprendizado por transferência consome menos recursos. Por isso, ele é muito adequado para o ambiente de navegação em dezenas de segundos em uma máquina moderna, não horas, dias ou semanas, para treinar todos os modelos.

Muito bem! Agora que você conhece a ideia principal do Transfer Learning, é hora de criar sua própria versão da Máquina que Aprende. Vamos começar.

5. Começar a configurar o código

Pré-requisitos

  • Um navegador da Web moderno.
  • Conhecimento básico de HTML, CSS, JavaScript e Chrome DevTools (visualizando a saída do console)

Vamos começar a programar

Modelos padronizados para começar foram criados para Glitch.com ou Codepen.io. Você pode simplesmente clonar qualquer modelo como seu estado base para este codelab, com apenas um clique.

No Glitch, clique no botão remixar isto para bifurcar e criar um novo conjunto de arquivos que você possa editar.

Como alternativa, no Codepen, clique em fork no canto inferior direito da tela.

Este 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 comodidade, há uma importação extra no arquivo HTML da biblioteca do TensorFlow.js. Fica assim:

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 o editor da Web da sua preferência ou trabalhe localmente

Se você quiser fazer o download do código e trabalhar localmente ou em outro editor on-line, basta criar os três arquivos nomeados acima no mesmo diretório e copiar e colar o código do código Glitch em cada um deles.

6. boilerplate HTML do app

Por onde começo?

Todos os protótipos exigem alguns suportes HTML básicos em que você pode renderizar suas descobertas. Configure agora. Você adicionará:

  • É um título para a página.
  • Texto descritivo.
  • Um parágrafo de status.
  • Um vídeo para armazenar o feed da webcam quando ele estiver pronto.
  • Vários botões para iniciar a câmera, coletar dados ou redefinir a experiência.
  • Importa para arquivos TensorFlow.js e JS que você codificará mais tarde.

Abra index.html e cole o código existente com as seguintes informações 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 &amp; 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>

Detalhar

Vamos detalhar parte do código HTML acima para destacar alguns dos principais itens que você adicionou.

  • Você adicionou uma tag <h1> para o título da página e uma tag <p> com um ID de "status", onde você imprimirá informações, usando diferentes partes do sistema para visualizar as saídas.
  • Você adicionou um elemento <video> com o ID "webcam" para renderizar o fluxo da webcam mais tarde.
  • Você adicionou cinco elementos <button>. A primeira, com o ID "enableCam", ativa a câmera. Os próximos dois botões têm uma classe "dataCollector", que permite coletar imagens de exemplo para os objetos que você quer reconhecer. O código que você criar posteriormente será projetado para que você possa adicionar qualquer um desses botões. Eles funcionarão como esperado automaticamente.

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 será usado 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ó podem funcionar com números.

Há também um atributo de nome de dados com o nome legível que você quer usar para essa classe. Isso permite fornecer um nome mais significativo ao usuário, em vez de um valor de índice numérico da primeira codificação.

Por fim, você tem um botão de treinar e redefinir para iniciar o processo de treinamento depois que os dados são coletados ou para redefinir o app, respectivamente.

  • Você também adicionou 2 importações do <script>. Uma para o TensorFlow.js e a outra para script.js que você definirá em breve.

7. Adic. estilo

Elementos padrão

Adicione estilos aos elementos HTML que você acabou de adicionar para garantir que eles sejam renderizados corretamente. Veja alguns estilos adicionados corretamente aos elementos de posição e tamanho. Nada demais. Você certamente poderá adicionar isso a ela mais tarde para criar uma UX ainda melhor, como vimos no vídeo de machine learning.

style.css (em inglês)

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 terá esta aparência:

81909685d7566dcb.png

8. JavaScript: constantes e listeners importantes

Definir constantes de chave

Primeiro, adicione algumas constantes importantes que você usará em todo o app. Comece substituindo o conteúdo de script.js por estas constantes:

script.js (em inglês)

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 = [];

Veja o que são:

  • O STATUS apenas contém uma referência à tag de parágrafo em que você gravará atualizações de status.
  • VIDEO contém uma referência ao elemento de vídeo HTML que renderizará o feed da webcam.
  • ENABLE_CAM_BUTTON, RESET_BUTTON e TRAIN_BUTTON buscam referências de DOM para todos os botões de chave da página HTML.
  • MOBILE_NET_INPUT_WIDTH e MOBILE_NET_INPUT_HEIGHT definem a largura e a altura de entrada esperadas do modelo do MobileNet, respectivamente. Ao armazená-la em uma constante próxima à parte superior do arquivo, desta forma, se você decidir usar uma versão diferente mais tarde, ficará mais fácil atualizar os valores de uma vez em vez de substituí-los em vários lugares.
  • STOP_DATA_GATHER está definido como - 1. Isso armazena um valor de estado para que você saiba quando o usuário tiver parado de clicar em um botão para coletar dados do feed da webcam. Esse nome mais significativo torna o código mais legível depois.
  • CLASS_NAMES atua como uma pesquisa e contém os nomes legíveis das possíveis previsões de classe. Essa matriz será preenchida posteriormente.

Agora que você tem referências aos elementos principais, está na hora de associar alguns listeners de eventos a eles.

Adicionar listeners de eventos importantes

Comece adicionando gerenciadores de eventos de clique aos botões de chave, conforme mostrado:

script.js (em inglês)

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 clicada.

TRAIN_BUTTON: chama trainAndPredict quando clicado.

RESET_BUTTON: as chamadas são redefinidas quando clicadas.

Por fim, nesta seção, você encontrará todos os botões que têm uma classe "dataCollector" usando document.querySelectorAll(). Isso retorna uma matriz de elementos encontrados no documento que corresponde:

script.js (em inglês)

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, você faz uma iteração com os botões encontrados e associa dois listeners de eventos a cada um deles. Um para "mousedown" e outro para "mouseup". Isso permite que você continue registrando amostras enquanto o botão estiver pressionado. Isso é útil para a coleta de dados.

Os dois eventos chamam uma função gatherDataForClass que você definirá posteriormente.

Neste ponto, você também pode enviar os nomes das classes legíveis encontradas pelo atributo data-name do botão HTML para a matriz CLASS_NAMES.

Em seguida, adicione algumas variáveis para armazenar itens essenciais que serão usados posteriormente.

script.js (em inglês)

let mobilenet = undefined;
let gatherDataState = STOP_DATA_GATHER;
let videoPlaying = false;
let trainingDataInputs = [];
let trainingDataOutputs = [];
let examplesCount = [];
let predict = false;

Vamos explicar cada uma delas.

Primeiro, você tem uma variável mobilenet para armazenar o modelo mobilenet carregado. Inicialmente, defina essa opção como indefinida.

Em seguida, você tem uma variável chamada gatherDataState. Se um botão "dataCollector" for pressionado, ele será alterado para um ID quente do botão, conforme definido no HTML, para que você saiba qual classe de dados está coletando no momento. Inicialmente, essa opção é definida como STOP_DATA_GATHER para que o loop de coleta de dados gravado posteriormente não colete dados quando nenhum botão estiver sendo pressionado.

O videoPlaying monitora se o stream da webcam foi carregado e reproduzido e está disponível para uso. Inicialmente, isso é definido como false porque a webcam só fica ativada quando você pressiona ENABLE_CAM_BUTTON..

Em seguida, defina duas matrizes, trainingDataInputs e trainingDataOutputs. Eles armazenam os valores de dados de treinamento coletados à medida que você clica nos botões "dataCollector" dos recursos de entrada gerados pelo modelo de base do MobileNet e na amostra de classe de saída, respectivamente.

Em seguida, uma matriz final, examplesCount,, é definida para acompanhar quantos exemplos estão contidos em cada classe quando você começa a adicioná-las.

Por fim, você tem uma variável chamada predict que controla o loop de previsão. Inicialmente, ele é definido como false. Nenhuma previsão pode ocorrer até que isso seja definido como true mais tarde.

Agora que todas as variáveis principais foram definidas, vamos carregar o modelo base MobileNet v3 pré-cortado que fornece vetores de recursos de imagem em vez de classificações.

9. Carregar o modelo base do MobileNet

Primeiro, defina uma nova função com o nome loadMobileNetFeatureModel, conforme mostrado abaixo. Ela precisa ser assíncrona, já que o ato de carregar um modelo é assíncrono:

script.js (em inglês)

/**
 * 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();

Nesse código, você define a URL em que o modelo a ser carregado está localizado na documentação do TFHub.

Em seguida, é possível carregar o modelo usando await tf.loadGraphModel(), lembrando de definir a propriedade especial fromTFHub como true enquanto você carrega um modelo do site do Google. Esse é um caso especial somente para usar modelos hospedados no TF Hub em que essa propriedade extra precisa ser definida.

Quando o carregamento for concluído, você poderá definir a innerText do elemento STATUS com uma mensagem para ver que ele foi carregado corretamente e começar a coletar dados.

Só é preciso aquecer o modelo. Com modelos maiores como esse, a primeira vez que você usa o modelo pode levar um tempo para configurar tudo. Portanto, é útil passar zeros pelo modelo para evitar qualquer espera no futuro em que o tempo pode ser mais crítico.

Você pode usar o tf.zeros() agrupado em um tf.tidy() para garantir que os tensores sejam descartados corretamente, com tamanho de lote de 1 e a altura e largura corretas definidas nas constantes no início. Por fim, você também especifica os canais de cor, que neste caso é 3, porque o modelo espera imagens RGB.

Em seguida, registre a forma resultante do tensor retornado usando answer.shape() para ajudar a entender o tamanho dos atributos da 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 sua visualização ao vivo agora, depois de alguns instantes, verá que o texto de status mudará de "Aguardando carregamento do TF.js" para "Monet Mobile v3 carregado com sucesso." conforme mostrado abaixo. Verifique se isso funciona antes de continuar.

a28b734e190afff.png

Também é possível verificar a saída do console para ver o tamanho impresso dos recursos de saída que esse modelo produz. Depois de executar zeros no modelo MobileNet, você verá uma forma de [1, 1024] impressa. O primeiro item é somente o tamanho do lote de 1. Como você pode ver, 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 a cabeça do modelo, que é essencialmente um perceptron multicamadas mínima.

script.js (em inglês)

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.Sequence a que você adicionará camadas de modelo.

Em seguida, adicione uma camada densa como a camada de entrada para esse modelo. Ela tem um formato de entrada 1024 porque as saídas dos recursos do MobileNet v3 são desse tamanho. Você descobriu isso na etapa anterior depois de transmiti-las pelo modelo. Essa camada tem 128 neurônios que usam a função de ativação ReLU.

Se você ainda não conhece as funções de ativação e as camadas de 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 de saída. O número de neurônios precisa ser igual ao número de classes que você está tentando prever. Para fazer isso, você pode usar CLASS_NAMES.length para descobrir quantas classes está planejando classificar, o que é igual ao número de botões de coleta de dados encontrados na interface do usuário. Como é um problema de classificação, use a ativação softmax nessa camada de saída, que precisa ser usada ao tentar criar um modelo para resolver problemas de classificação em vez de regressão.

Agora, exiba uma model.summary() para exibir a visão geral do modelo recém-definido no console.

Por fim, compile o modelo para que ele esteja pronto para o treinamento. 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 precisão também são solicitadas para que possam ser monitoradas nos registros posteriormente para fins de depuração.

No console, você verá algo parecido com isto:

22eaf32286fea4bb.png.

Esse recurso tem mais de 130 mil parâmetros treináveis. Mas como esta é uma camada densa simples de neurônios regulares, ela será treinada rapidamente.

Como uma atividade a ser feita após a conclusão do projeto, você pode tentar mudar o número de neurônios na primeira camada para ver o nível de capacidade dela e, ao mesmo tempo, manter um desempenho satisfatório. Muitas vezes, com o aprendizado de máquina, há algum nível de tentativa e erro para encontrar valores de parâmetro ideais para oferecer o melhor equilíbrio entre uso de recursos e velocidade.

11. Ativar a webcam

Agora, é hora de detalhar a função enableCam() definida anteriormente. 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 (em inglês)

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 com o nome hasGetUserMedia() para verificar se o navegador é compatível com getUserMedia(). Para isso, verifique a existência das principais propriedades das APIs do navegador.

Na função enableCam(), use a função hasGetUserMedia() que você definiu acima para verificar se ela é compatível. Caso contrário, exiba um aviso no console.

Se for compatível, defina algumas restrições para a chamada de getUserMedia(), como fazer o stream de vídeo somente, preferir que width do vídeo tenha 640 pixels de tamanho e o elemento height para 480 pixels. Por que fazer isso? Não há sentido em fazer um vídeo maior que isso. Ele precisaria ser redimensionado para 224 por 224 pixels para ser inserido no modelo do MobileNet. Você também pode economizar alguns recursos de computação solicitando uma resolução menor. A maioria das câmeras é compatível com uma resolução desse tamanho.

Em seguida, chame navigator.mediaDevices.getUserMedia() com os constraints detalhados acima e aguarde o retorno de stream. Quando o stream for retornado, você poderá fazer com que o elemento VIDEO reproduza o stream, definindo-o como o valor srcObject.

Você também precisa adicionar um eventListener no elemento VIDEO para saber quando o stream foi carregado e está sendo reproduzido.

Quando o vapor for carregado, defina videoPlaying como "true" e remova ENABLE_CAM_BUTTON para impedir 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 sua primeira vez fazendo isso, você verá a renderização do elemento de vídeo na página como mostrado:

b378eb1affa9b883.png

Agora, é hora de adicionar uma função para processar os cliques no botão dataCollector.

12. Manipulador de eventos do botão de coleta de dados

Agora é hora de preencher a função vazia atual, chamada gatherDataForClass().. É isso que você atribuiu como função de manipulador de eventos para botões dataCollector no início do codelab.

script.js (em inglês)

/**
 * 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 chamando this.getAttribute() com o nome do atributo. Neste caso, data-1hot como o parâmetro. Como esta é uma string, use parseInt() para transmitir a um número inteiro e atribuir esse resultado a uma variável com o nome classNumber..

Em seguida, defina a variável gatherDataState conforme necessário. Se a gatherDataState atual for igual a STOP_DATA_GATHER (que você definiu como -1), isso significa que você não está coletando dados e que um evento mousedown foi disparado. Defina a gatherDataState como a classNumber que você acabou de encontrar.

Caso contrário, isso significa que você está coletando dados, e o evento acionado foi um evento mouseup, e agora quer parar de coletar dados dessa classe. Volte ao estado STOP_DATA_GATHER para encerrar o loop de coleta de dados que você definirá em breve.

Por fim, inicie a chamada para dataGatherLoop(),, que executa a gravação de dados da classe.

13. Coleta de dados

Agora, defina a função dataGatherLoop(). Essa função é responsável por coletar amostras de imagens do vídeo, transmitindo-as pelo modelo do MobileNet e capturando as saídas desse modelo (os vetores de recursos de 1.024).

Em seguida, ele os armazena 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 (em inglês)

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ó continuará a execução dessa função se videoPlaying for verdadeiro, o que significa que a webcam está ativa e gatherDataState não é igual a STOP_DATA_GATHER e um botão para coleta de dados da classe está sendo pressionado.

Em seguida, envolva 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 ter a forma correta para a entrada do modelo do MobileNet. Use uma chamada tf.image.resizeBilinear() com o tensor que você quer reformular como o primeiro parâmetro. Em seguida, use uma forma que defina a nova altura e largura, como definido pelas constantes já criadas. Por fim, defina os cantos de alinhamento como verdadeiros 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 sua imagem da webcam tem 640 x 480 pixels, e o modelo precisa de uma imagem quadrada de 224 por 224 pixels.

Para esta demonstração, isso deve funcionar. Depois de concluir este codelab, você pode tentar cortar uma imagem quadrada para melhorar ainda mais os resultados de qualquer sistema de produção que possa criar mais tarde.

Em seguida, normalize os dados da imagem. Os dados de imagem estão sempre no intervalo de 0 a 255 quando você usa tf.browser.frompixels(). Assim, você pode simplesmente redimensionar TensorFrame por 255, para garantir que todos os valores fiquem entre 0 e 1, que é o que o modelo do MobileNet espera como entradas.

Por fim, na seção tf.tidy() do código, envie o tensor normal pelo modelo carregado chamando mobilenet.predict(), para o qual você transmite a versão expandida do normalizedTensorFrame usando expandDims(). um lote de 1, porque o modelo espera um lote de entradas para processamento.

Quando o resultado for retornado, será possível chamarsqueeze() no resultado retornado e comprimido para um tensor 1D, que você retorna e atribui àimageFeatures que captura o resultado detf.tidy() de dados.

Agora que você tem a imageFeatures do modelo MobileNet, pode registrá-la enviando-a para a matriz trainingDataInputs definida anteriormente.

Você também pode registrar o que essa entrada representa enviando a gatherDataState atual para a matriz trainingDataOutputs.

A variável gatherDataState seria definida como o ID numérico da classe atual em que você está registrando dados quando o botão foi clicado na função gatherDataForClass() definida anteriormente.

Agora também é possível aumentar o número de exemplos para uma determinada classe. Para fazer isso, primeiro confira se o índice na matriz examplesCount foi inicializado antes ou não. Se não estiver definido, defina-o como 0 para inicializar o contador para o ID numérico de determinada classe. Dessa forma, será possível incrementar a examplesCount para o gatherDataState atual.

Agora, atualize o texto do elemento STATUS na página da Web para mostrar as contagens de cada classe à medida que elas são capturadas. Para fazer isso, faça uma repetição na matriz CLASS_NAMES e exiba o nome legível 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 novamente de forma recursiva. Isso continuará gerando amostras do frame do vídeo até que o mouseup do botão seja detectado e gatherDataState seja definido como STOP_DATA_GATHER,. O loop de coleta de dados terminará.

Se você executar o código agora, poderá clicar no botão para ativar a câmera, aguardar o carregamento da webcam e, em seguida, clicar e manter pressionado cada botão de coleta de dados para coletar exemplos de cada classe. Aqui você vê como faço para coletar os dados do meu smartphone e da minha mão, respectivamente.

541051644a45131f.gif

Você verá o texto de status atualizado porque armazena todos os tensores na memória, como mostra a captura de tela acima.

14. Treinar e prever

A próxima etapa é implementar o código da função trainAndPredict() atualmente vazia, que é onde ocorre o aprendizado por transferência. Vamos dar uma olhada no código:

script.js (em inglês)

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 ser um tensor1d do tipo int32. Assim, ela estará pronta para ser usada em uma uma codificação de uso intenso. Isso é armazenado em uma variável chamada outputsAsTensor.

Use a função tf.oneHot() com essa variável outputsAsTensor junto com o número máximo de classes a serem codificadas, que é apenas o CLASS_NAMES.length. As saídas codificadas agora são armazenadas em um novo tensor chamado oneHotOutputs.

Observe que, no momento, trainingDataInputs é uma matriz de tensores gravados. Para usá-los no treinamento, será preciso converter a matriz de tensores em um tensor 2D normal.

Para isso, há uma ótima função dentro da biblioteca do TensorFlow.js chamada tf.stack():

que usa uma matriz de tensores e os empilha para produzir um tensor de maior dimensão como saída. Neste caso, um tensor 2D é retornado, isto é, um lote de entradas dimensionais com comprimento de 1.024 contendo os atributos gravados, que é o necessário para o treinamento.

Em seguida, use await model.fit() para treinar o cabeçalho do modelo personalizado. Transmita a variável inputsAsTensor com a oneHotOutputs para representar os dados de treinamento a serem usados, por exemplo, entradas e saídas de destino, respectivamente. No objeto de configuração do terceiro parâmetro, definashuffle atrue , usebatchSize de5 , comepochs Definir como10 e especifique umcallback poronEpochEnd à páginalogProgress que você definirá em breve.

Por fim, é possível descartar os tensores criados porque o modelo está treinado. Você pode definir predict novamente como true para permitir que as previsões ocorram novamente. Depois, chame a função predictLoop() para começar a prever imagens em tempo real da webcam.

Também é possível definir a função logProcess() para registrar o estado do treinamento, que é usado acima, model.fit(), e exibir os resultados no console após cada rodada de treinamento.

Falta pouco. 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 analisa frames de uma webcam e prevê continuamente o que está em cada frame com resultados em tempo real no navegador.

Vamos verificar o código:

script.js (em inglês)

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, é possível ver os recursos de imagem para a imagem atual como você fez na função dataGatherLoop(). Basicamente, você pegará um frame com a webcam usando tf.browser.from pixels(), normalizá-lo, redimensionar para 224 x 224 pixels e transmitir esses dados pelo modelo MobileNet para ter os recursos de imagem resultantes.

No entanto, agora é possível usar a cabeça do modelo recém-treinado para realizar uma previsão, transmitindo o imageFeatures resultante encontrado recentemente pela função predict() do modelo treinado. É possível apertar 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 valor mais alto usando argMax() e, em seguida, converter esse tensor resultante em uma matriz usando arraySync() para chegar aos dados subjacentes no JavaScript para descobrir a posição da elemento de maior valor. Esse valor é armazenado na variável chamada highestIndex.

Também é possível receber as pontuações reais 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 de STATUS com os dados de prediction. Para acessar a string legível para a classe, basta consultar o highestIndex na matriz CLASS_NAMES e, em seguida, capturar o valor de confiança no predictionArray. Para torná-lo mais legível em porcentagem, basta multiplicar por 100 e math.floor() o resultado.

Por fim, use a window.requestAnimationFrame() para chamar predictionLoop() de novo quando estiver tudo pronto. Assim, você recebe uma classificação em tempo real no seu stream de vídeo. Isso continuará até que predict esteja definido como false se você optar por treinar um novo modelo com novos dados.

Isso leva você à peça final do quebra-cabeça. Como implementar o botão de redefinição.

15. Implementar o botão de redefinição

Quase concluído! A última parte do quebra-cabeça é implementar um botão de redefinição para recomeçar. O código da função reset() atualmente vazia está abaixo. Faça as seguintes atualizações:

script.js (em inglês)

/**
 * 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 qualquer loop de previsão em execução definindo predict como false. Em seguida, exclua todo o conteúdo da matriz examplesCount definindo o comprimento como 0, o que é uma maneira útil de limpar todo o conteúdo de uma matriz.

Agora, confira todos os trainingDataInputs registrados atualmente e garanta que dispose() de cada tensor dentro dele para liberar memória novamente, já que os tensores não são limpos pelo coletor de lixo do JavaScript.

Depois de fazer isso, agora você pode definir com segurança o comprimento da matriz como 0 nas matrizes trainingDataInputs e trainingDataOutputs para limpar esses dados também.

Por fim, defina o texto STATUS como algo sensível e exiba os tensores deixados na memória como uma verificação de integridade.

Haverá algumas centenas de tensores ainda na memória, já que o modelo MobileNet e o periférico multicamadas que você definiu não são descartados. Será necessário reutilizá-los com novos dados de treinamento se você decidir treinar novamente após a redefinição.

16. Faça um teste

É hora de testar sua própria versão da Máquina que Aprende!

Vá para a visualização ao vivo, ative a webcam, reúna pelo menos 30 amostras da classe 1 de algum objeto na sala e faça o mesmo para a classe 2 com outro objeto, clique em "Treinar" e verifique o registro do console para ver o progresso. Ele será treinado rapidamente:

bf1ac3cc5b15740.gif

Após o treinamento, mostre os objetos da câmera para receber previsões em tempo real que serão impressas na área de texto do 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 não copiou.

17. Parabéns

Parabéns! Você acabou de concluir seu primeiro exemplo de aprendizado por transferência usando o TensorFlow.js ao vivo no navegador.

Faça um teste, teste em vários objetos e observe que algumas coisas são mais difíceis de reconhecer do que outras, especialmente se forem semelhantes a alguma outra coisa. Talvez seja necessário adicionar mais classes ou dados de treinamento para diferenciá-los.

Resumo

Neste codelab, você aprendeu:

  1. O que é aprendizado por transferência e as vantagens dele em relação ao treinamento de um modelo completo.
  2. Como conseguir modelos para reutilizar no TensorFlow Hub.
  3. Como configurar um app da Web adequado para aprendizado por transferência
  4. Como carregar e usar um modelo base para gerar recursos de imagem.
  5. Como treinar um novo cabeçalho de previsão que reconheça objetos personalizados de imagens de webcam.
  6. 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 machine learning para um caso de uso real? Talvez você possa revolucionar o setor em que trabalha para ajudar as pessoas na sua empresa a treinar modelos para classificar o que é importante no trabalho diário? As possibilidades são infinitas.

Para continuar, considere fazer este curso completo gratuitamente, que mostra como combinar os dois modelos que você tem neste codelab em um único modelo para aumentar a eficiência.

Se você quiser saber mais sobre a teoria por trás do aplicativo original, confira este tutorial.

Compartilhe o que você sabe conosco

É fácil estender o que você fez hoje para outros casos de uso de criativo. Além disso, recomendamos que você pense fora da caixa e continue a invadir.

Lembre-se de nos marcar nas mídias sociais usando a hashtag #MadeWithTFJS para que seu projeto tenha a chance de ser destacado no nosso blog do TensorFlow ou até mesmo em eventos futuros. Adoraríamos ver o que você fará.

Sites para conferir