TensorFlow.js funciona no navegador e no Node.js, e em ambas as plataformas há muitas configurações diferentes disponíveis. Cada plataforma possui um conjunto exclusivo de considerações que afetarão a forma como os aplicativos são desenvolvidos.
No navegador, TensorFlow.js oferece suporte a dispositivos móveis e também a dispositivos de desktop. Cada dispositivo possui um conjunto específico de restrições, como APIs WebGL disponíveis, que são determinadas e configuradas automaticamente para você.
No Node.js, o TensorFlow.js oferece suporte à vinculação direta à API do TensorFlow ou à execução com implementações de CPU vanilla mais lentas.
Ambientes
Quando um programa TensorFlow.js é executado, a configuração específica é chamada de ambiente. O ambiente é composto por um único back-end global, bem como por um conjunto de sinalizadores que controlam recursos detalhados do TensorFlow.js.
Back-ends
TensorFlow.js oferece suporte a vários back-ends diferentes que implementam armazenamento de tensor e operações matemáticas. A qualquer momento, apenas um back-end está ativo. Na maioria das vezes, o TensorFlow.js escolherá automaticamente o melhor back-end para você, de acordo com o ambiente atual. No entanto, às vezes é importante saber qual back-end está sendo usado e como alterá-lo.
Para descobrir qual back-end você está usando:
console.log(tf.getBackend());
Se você quiser alterar manualmente o back-end:
tf.setBackend('cpu');
console.log(tf.getBackend());
Back-end WebGL
O backend WebGL, 'webgl', é atualmente o backend mais poderoso para o navegador. Este back-end é até 100x mais rápido que o back-end da CPU vanilla. Os tensores são armazenados como texturas WebGL e as operações matemáticas são implementadas em shaders WebGL. Aqui estão algumas coisas úteis que você deve saber ao usar este back-end: \
Evite bloquear o thread da UI
Quando uma operação é chamada, como tf.matMul(a, b), o tf.Tensor resultante é retornado de forma síncrona, porém o cálculo da multiplicação da matriz pode ainda não estar pronto. Isso significa que o tf.Tensor retornado é apenas um identificador para o cálculo. Quando você chama x.data()
ou x.array()
, os valores serão resolvidos quando o cálculo for realmente concluído. Isso torna importante usar os métodos assíncronos x.data()
e x.array()
em vez de seus equivalentes síncronos x.dataSync()
e x.arraySync()
para evitar o bloqueio do thread da UI enquanto o cálculo é concluído.
Gerenciamento de memória
Uma ressalva ao usar o backend WebGL é a necessidade de gerenciamento explícito de memória. WebGLTextures, que é onde os dados do Tensor são armazenados, não são automaticamente coletados como lixo pelo navegador.
Para destruir a memória de um tf.Tensor
, você pode usar o método dispose()
:
const a = tf.tensor([[1, 2], [3, 4]]);
a.dispose();
É muito comum encadear múltiplas operações em uma aplicação. Manter uma referência a todas as variáveis intermediárias para descartá-las pode reduzir a legibilidade do código. Para resolver esse problema, TensorFlow.js fornece um método tf.tidy()
que limpa todos tf.Tensor
s que não são retornados por uma função após executá-la, semelhante à forma como as variáveis locais são limpas quando uma função é executada:
const a = tf.tensor([[1, 2], [3, 4]]);
const y = tf.tidy(() => {
const result = a.square().log().neg();
return result;
});
Precisão
Em dispositivos móveis, o WebGL pode suportar apenas texturas de ponto flutuante de 16 bits. No entanto, a maioria dos modelos de aprendizado de máquina são treinados com ativações e pesos de ponto flutuante de 32 bits. Isso pode causar problemas de precisão ao portar um modelo para um dispositivo móvel, pois números flutuantes de 16 bits só podem representar números no intervalo [0.000000059605, 65504]
. Isso significa que você deve ter cuidado para que os pesos e ativações em seu modelo não excedam essa faixa. Para verificar se o dispositivo suporta texturas de 32 bits, verifique o valor de tf.ENV.getBool('WEBGL_RENDER_FLOAT32_CAPABLE')
, se for falso, o dispositivo suporta apenas texturas de ponto flutuante de 16 bits. Você pode usar tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED')
para verificar se o TensorFlow.js está usando texturas de 32 bits.
Compilação de shaders e uploads de texturas
TensorFlow.js executa operações na GPU executando programas de sombreamento WebGL. Esses shaders são montados e compilados lentamente quando o usuário solicita a execução de uma operação. A compilação de um shader acontece na CPU do thread principal e pode ser lenta. O TensorFlow.js armazenará em cache os shaders compilados automaticamente, tornando a segunda chamada para a mesma operação com tensores de entrada e saída do mesmo formato muito mais rápida. Normalmente, os aplicativos TensorFlow.js usarão as mesmas operações várias vezes durante a vida útil do aplicativo, portanto, a segunda passagem por um modelo de aprendizado de máquina é muito mais rápida.
TensorFlow.js também armazena dados tf.Tensor como WebGLTextures. Quando um tf.Tensor
é criado, não carregamos os dados imediatamente para a GPU, mas mantemos os dados na CPU até que o tf.Tensor
seja usado em uma operação. Se o tf.Tensor
for usado uma segunda vez, os dados já estarão na GPU, portanto não haverá custo de upload. Em um modelo típico de aprendizado de máquina, isso significa que os pesos são carregados durante a primeira previsão pelo modelo e a segunda passagem pelo modelo será muito mais rápida.
Se você se preocupa com o desempenho da primeira previsão por meio de seu modelo ou código TensorFlow.js, recomendamos aquecer o modelo passando um Tensor de entrada do mesmo formato antes que os dados reais sejam usados.
Por exemplo:
const model = await tf.loadLayersModel(modelUrl);
// Warmup the model before using real data.
const warmupResult = model.predict(tf.zeros(inputShape));
warmupResult.dataSync();
warmupResult.dispose();
// The second predict() will be much faster
const result = model.predict(userData);
Back-end do TensorFlow em Node.js.
No back-end do TensorFlow Node.js, 'node', a API TensorFlow C é usada para acelerar as operações. Isso usará a aceleração de hardware disponível na máquina, como CUDA, se disponível.
Neste backend, assim como no backend WebGL, as operações retornam tf.Tensor
s de forma síncrona. No entanto, ao contrário do back-end WebGL, a operação é concluída antes de você recuperar o tensor. Isso significa que uma chamada para tf.matMul(a, b)
bloqueará o thread da UI.
Por esse motivo, se você pretende usar isso em um aplicativo de produção, execute TensorFlow.js em threads de trabalho para não bloquear o thread principal.
Para obter mais informações sobre Node.js, consulte este guia.
back-end WASM
TensorFlow.js fornece um back-end WebAssembly ( wasm
), que oferece aceleração de CPU e pode ser usado como uma alternativa aos back-ends vanilla JavaScript CPU ( cpu
) e WebGL acelerados ( webgl
). Para usá-lo:
// Set the backend to WASM and wait for the module to be ready.
tf.setBackend('wasm');
tf.ready().then(() => {...});
Se o seu servidor estiver servindo o arquivo .wasm
em um caminho ou nome diferente, use setWasmPath
antes de inicializar o backend. Consulte a seção "Usando Bundlers" no README para obter mais informações:
import {setWasmPath} from '@tensorflow/tfjs-backend-wasm';
setWasmPath(yourCustomPath);
tf.setBackend('wasm');
tf.ready().then(() => {...});
Por que WASM?
WASM foi introduzido em 2015 como um novo formato binário baseado na web, fornecendo programas escritos em JavaScript, C, C++, etc., um alvo de compilação para execução na web. WASM é compatível com Chrome, Safari, Firefox e Edge desde 2017 e é compatível com 90% dos dispositivos em todo o mundo.
Desempenho
O back-end WASM aproveita a biblioteca XNNPACK para implementação otimizada de operadores de redes neurais.
Versus JavaScript : Os binários WASM são geralmente muito mais rápidos do que os pacotes JavaScript para os navegadores carregarem, analisarem e executarem. O JavaScript é digitado dinamicamente e coletado como lixo, o que pode causar lentidão no tempo de execução.
Versus WebGL : WebGL é mais rápido que WASM para a maioria dos modelos, mas para modelos pequenos o WASM pode superar o WebGL devido aos custos indiretos fixos de execução de shaders WebGL. A seção “Quando devo usar WASM” abaixo discute heurísticas para tomar essa decisão.
Portabilidade e Estabilidade
WASM possui aritmética flutuante portátil de 32 bits, oferecendo paridade precisa em todos os dispositivos. O WebGL, por outro lado, é específico do hardware e diferentes dispositivos podem ter precisão variável (por exemplo, fallback para floats de 16 bits em dispositivos iOS).
Assim como o WebGL, o WASM é oficialmente suportado por todos os principais navegadores. Ao contrário do WebGL, o WASM pode ser executado em Node.js e usado no lado do servidor sem qualquer necessidade de compilar bibliotecas nativas.
Quando devo usar o WASM?
Tamanho do modelo e demanda computacional
Em geral, WASM é uma boa escolha quando os modelos são menores ou você se preocupa com dispositivos de baixo custo que não têm suporte para WebGL (extensão OES_texture_float
) ou têm GPUs menos potentes. O gráfico abaixo mostra os tempos de inferência (a partir do TensorFlow.js 1.5.2) no Chrome em um MacBook Pro 2018 para 5 de nossos modelos oficialmente suportados nos back-ends WebGL, WASM e CPU:
Modelos menores
Modelo | WebGL | WASM | CPU | Memória |
---|---|---|---|---|
BlazeFace | 22,5ms | 15,6ms | 315,2ms | 0,4MB |
FaceMesh | 19,3ms | 19,2ms | 335ms | 2,8MB |
Modelos maiores
Modelo | WebGL | WASM | CPU | Memória |
---|---|---|---|---|
PoseNet | 42,5ms | 173,9ms | 1514,7ms | 4,5MB |
BodyPix | 77ms | 188,4ms | 2683ms | 4,6MB |
MobileNet v2 | 37ms | 94ms | 923,6ms | 13MB |
A tabela acima mostra que o WASM é 10-30x mais rápido do que o back-end de CPU JS simples em todos os modelos e competitivo com o WebGL para modelos menores como BlazeFace , que é leve (400 KB), mas tem um número razoável de operações (~ 140). Dado que os programas WebGL têm um custo indireto fixo por execução de operação, isso explica por que modelos como o BlazeFace são mais rápidos no WASM.
Esses resultados irão variar dependendo do seu dispositivo. A melhor maneira de determinar se o WASM é adequado para sua aplicação é testá-lo em nossos diferentes back-ends.
Inferência vs Treinamento
Para abordar o caso de uso principal para implantação de modelos pré-treinados, o desenvolvimento de back-end WASM priorizará a inferência em vez do suporte ao treinamento . Veja uma lista atualizada de operações suportadas no WASM e informe-nos se o seu modelo tiver uma operação não suportada. Para modelos de treinamento, recomendamos usar o backend Node (TensorFlow C++) ou o backend WebGL.
Back-end da CPU
O back-end da CPU, 'cpu', é o back-end de menor desempenho, porém é o mais simples. As operações são todas implementadas em JavaScript vanilla, o que as torna menos paralelizáveis. Eles também bloqueiam o thread da UI.
Este backend pode ser muito útil para testes ou em dispositivos onde o WebGL não está disponível.
Bandeiras
TensorFlow.js possui um conjunto de sinalizadores de ambiente que são avaliados automaticamente e determinam a melhor configuração na plataforma atual. Esses sinalizadores são em sua maioria internos, mas alguns sinalizadores globais podem ser controlados com API pública.
-
tf.enableProdMode():
ativa o modo de produção, que removerá a validação do modelo, verificações NaN e outras verificações de correção em favor do desempenho. -
tf.enableDebugMode()
: ativa o modo de depuração, que registrará no console cada operação executada, bem como informações de desempenho em tempo de execução, como consumo de memória e tempo total de execução do kernel. Observe que isso tornará seu aplicativo muito mais lento, não use isso na produção.