1. Antes de começar
Neste codelab, você aprenderá a executar uma inferência de classificação de imagem de um site usando o TensorFlow Serving com REST e gRPC.
Prerequisites
- Conhecimento básico de desenvolvimento para Web, como HTML e JavaScript
- Conhecimento básico de machine learning com o TensorFlow, como treinamento e implantação
- Conhecimento básico de terminais e Docker
O que você aprenderá
- Como encontrar modelos de classificação de imagens pré-treinados no TensorFlow Hub.
- Como criar um site simples e fazer previsões com o modelo de classificação de imagem transferido por download usando o TensorFlow Serving (REST e gRPC).
- Como renderizar o resultado da detecção na IU.
Pré-requisitos
- Docker
- Google Chrome
- Servidor da Web para Chrome
- Node.js e NPM
- Bash
- Compilador de buffer de protocolo (somente necessário se você quiser gerar o stub do gRPC novamente)
- Plug-in do gerador de código gRPC na Web (necessário apenas se você quiser gerar o stub de gRPC novamente)
2. Começar a configuração
Para fazer o download do código para este codelab, faça o seguinte:
- Navegue até este repositório do GitHub.
- Clique em Code > Download zip para fazer o download de todo o código para este codelab.
- Descompacte o arquivo ZIP transferido por download para descompactar uma pasta raiz do
codelabs
com todos os recursos que você precisa.
Neste codelab, você só precisará dos arquivos no subdiretório TFServing/ImageClassificationWeb
do repositório, que contém duas pastas:
- A pasta
starter
contém o código inicial que você usará como base para este codelab. - A pasta
finished
contém o código concluído do app de exemplo finalizado.
3. Instale as dependências
Para instalar as dependências:
- No seu terminal, navegue até a pasta
starter
e instale os pacotes NPM necessários:
npm install
4. Executar o site inicial
Use o Web Server for Chrome para carregar o arquivo TFServing/ImageClassificationWeb/starter/dist/index.html
:
- Digite
Chrome://apps/
na barra de endereços do Chrome e localize Web Server for Chrome na lista de aplicativos. - Inicie o servidor da Web para o Chrome e, em seguida, escolha a pasta
TFServing/ImageClassificationWeb/starter/dist/
. - Clique no botão Web Server para ativá-lo e navegue até http://localhost:8887/ no navegador.
Executar e explorar o site
Você verá o site agora. A IU é muito simples: há uma imagem de gato em que você quer classificar e o usuário pode enviar os dados para o back-end com REST ou gRPC. O back-end realiza a classificação da imagem e retorna o resultado da classificação para o site, que exibe o resultado.
Se você clicar em Classificar, nada acontecerá porque ainda não há comunicação com o back-end.
5. Implante um modelo de classificação de imagens com o TensorFlow Serving
A classificação de imagens é uma tarefa muito comum de ML que classifica imagens em categorias predefinidas com base no conteúdo principal delas. Veja um exemplo de classificação de flores:
Há vários modelos de classificação de imagens pré-treinados no TensorFlow Hub. Você usa um modelo conhecido do Inception v3 para este codelab.
Para implantar o modelo de classificação de imagem com o TensorFlow Serving:
- Faça o download do arquivo de modelo Inception v3.
- Descompacte o arquivo
.tar.gz
salvo com uma ferramenta de descompactação, como o 7-Zip. - Crie uma pasta
inception_v3
e uma subpasta123
dentro dela. - Coloque a pasta
variables
extraída e o arquivosaved_model.pb
na subpasta123
.
Você pode se referir à pasta inception_v3
como a pasta SavedModel
. 123
é um exemplo de número da versão. Se quiser, você pode escolher outro número.
A estrutura de pasta será semelhante a esta imagem:
Iniciar o TensorFlow Serving
- No seu terminal, inicie o TensorFlow Serving com o Docker, mas substitua
PATH/TO/SAVEDMODEL
pelo caminho absoluto da pastainception_v3
no seu computador.
docker pull tensorflow/serving docker run -it --rm -p 8500:8500 -p 8501:8501 -v "PATH/TO/SAVEDMODEL:/models/inception" -e MODEL_NAME=inception tensorflow/serving
Primeiro, o Docker faz o download automático da imagem do TensorFlow Serving em um minuto. Depois disso, o TensorFlow Serving será iniciado. O registro será semelhante a este snippet de código:
2022-02-25 06:01:12.513231: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle. 2022-02-25 06:01:12.585012: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 3000000000 Hz 2022-02-25 06:01:13.395083: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/inception/123 2022-02-25 06:01:13.837562: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 1928700 microseconds. 2022-02-25 06:01:13.877848: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/inception/123/assets.extra/tf_serving_warmup_requests 2022-02-25 06:01:13.929844: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: inception version: 123} 2022-02-25 06:01:13.985848: I tensorflow_serving/model_servers/server_core.cc:486] Finished adding/updating models 2022-02-25 06:01:13.985987: I tensorflow_serving/model_servers/server.cc:367] Profiler service is enabled 2022-02-25 06:01:13.988994: I tensorflow_serving/model_servers/server.cc:393] Running gRPC ModelServer at 0.0.0.0:8500 ... [warn] getaddrinfo: address family for nodename not supported 2022-02-25 06:01:14.033872: I tensorflow_serving/model_servers/server.cc:414] Exporting HTTP/REST API at:localhost:8501 ... [evhttp_server.cc : 245] NET_LOG: Entering the event loop ...
6. Configurar o proxy Envoy
Atualmente, o TensorFlow Serving não define o cabeçalho Access-Control-Allow-Origin
. Portanto, o navegador bloqueia a solicitação do JavaScript de front-end para o TensorFlow Serving por motivos de segurança. Para contornar esse problema, você precisa usar um proxy, como o Envoy, para enviar a solicitação do JavaScript pelo back-end do TensorFlow Serving.
Iniciar Envoy
- No seu terminal, faça o download da imagem do Envoy e inicie o Envoy com o Docker. Substitua o marcador
PATH/TO/ENVOY-CUSTOM.YAML
pelo caminho absoluto do arquivoenvoy-custom.yaml
na pastastarter
.
docker pull envoyproxy/envoy-dev:fd3e8370ddb7a96634c192d1461516e6de1d1797 docker run --add-host host.docker.internal:host-gateway --rm -it -p 9901:9901 -p 8000:8000 -p 8080:8080 -v PATH/TO/ENVOY-CUSTOM.YAML:/envoy-custom.yaml envoyproxy/envoy-dev:fd3e8370ddb7a96634c192d1461516e6de1d1797 -c /envoy-custom.yaml
Primeiro, o Docker faz o download automático da imagem do Envoy. Depois disso, o Envoy será iniciado. O registro será semelhante a este snippet de código:
[2022-03-02 07:51:48.563][1][info][main] [source/server/server.cc:436] response trailer map: 152 bytes: grpc-message,grpc-status [2022-03-02 07:51:48.681][1][info][main] [source/server/server.cc:772] runtime: {} [2022-03-02 07:51:48.682][1][info][admin] [source/server/admin/admin.cc:134] admin address: 0.0.0.0:9901 [2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:127] loading tracing configuration [2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:87] loading 0 static secret(s) [2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:93] loading 2 cluster(s) [2022-03-02 07:51:48.687][1][info][config] [source/server/configuration_impl.cc:97] loading 2 listener(s) [2022-03-02 07:51:48.694][1][info][config] [source/server/configuration_impl.cc:109] loading stats configuration [2022-03-02 07:51:48.696][1][info][main] [source/server/server.cc:868] starting main dispatch loop [2022-03-02 07:51:48.881][1][info][runtime] [source/common/runtime/runtime_impl.cc:446] RTDS has finished initialization [2022-03-02 07:51:48.881][1][info][upstream] [source/common/upstream/cluster_manager_impl.cc:207] cm init: all clusters initialized [2022-03-02 07:51:48.881][1][info][main] [source/server/server.cc:849] all clusters initialized. initializing init manager [2022-03-02 07:51:48.881][1][info][config] [source/server/listener_manager_impl.cc:784] all dependencies initialized. starting workers [2022-03-02 07:51:48.902][1][warning][main] [source/server/server.cc:747] there is no configured limit to the number of allowed active connections. Set a limit via the runtime key overload.global_downstream_max_connections
7. Conectar o site ao TensorFlow usando a API REST
O back-end está pronto para que você possa enviar solicitações de cliente ao TensorFlow Serving para classificar imagens. Há duas maneiras de enviar solicitações ao TensorFlow Serving:
- REST
- gRPC
Enviar solicitações e receber respostas pelo REST
Há três etapas simples para enviar e receber solicitações pela REST:
- Crie a solicitação REST.
- Envie a solicitação REST para o TensorFlow Serving.
- Extraia o resultado previsto da resposta REST e exiba-o.
Você realiza essas etapas no arquivo src/index.js
.
Criar a solicitação REST
No momento, a função classify_img()
não envia a solicitação REST para o TensorFlow Serving. Primeiro, implemente essa ramificação da REST para criar uma solicitação REST:
if (radioButtons[0].checked) {
console.log('Using REST');
// TODO: Add code to send a REST request to TensorFlow Serving.
}
O TensorFlow Serving espera uma solicitação POST que contenha o tensor de imagem do modelo do Inception v3 que você usa. Portanto, você precisa extrair os valores RGB de cada pixel da imagem em uma matriz e, em seguida, unir a matriz em um JSON, que é o payload da solicitação.
- Adicione este código à ramificação REST:
//Create the REST request.
let imgTensor = new Array();
let pixelArray = new Array();
context.drawImage(img, 0, 0);
for(let i=0; i<inputImgHeight; i++) {
pixelArray[i] = new Array();
for (let j=0; j<inputImgWidth; j++) {
pixelArray[i][j] = new Array();
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[0]/255);
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[1]/255);
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[2]/255);
}
}
imgTensor.push(pixelArray);
const RESTURL = 'http://localhost:8000/v1/models/inception:predict';
let xhr = new XMLHttpRequest();
xhr.open('POST', RESTURL);
xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8;');
let data = JSON.stringify({
instances: imgTensor
});
xhr.onload = () => {
}
xhr.onerror = () => {
console.log('REST request error');
}
Enviar a solicitação REST para o TensorFlow Serving
Agora você pode enviar a solicitação.
- Adicione este código logo após o código acima na ramificação REST:
// Send the REST request.
xhr.send(data);
Processar a resposta REST do TensorFlow Serving
O modelo do Inception v3 retorna uma matriz de probabilidades de que a imagem pertence a categorias predefinidas. Quando a previsão for bem-sucedida, você deve gerar a categoria mais provável na IU.
Implemente o listener onload()
para processar a resposta.
xhr.onload = () => {
}
- Adicione este código ao listener
onload()
:
// Process the REST response.
const response = JSON.parse(xhr.responseText);
const maxIndex = argmax(response['predictions'][0])
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
Agora, o listener extrai as probabilidades previstas da resposta, identifica a categoria mais provável do objeto e exibe o resultado na IU.
Executar
- No terminal, acesse a pasta
starter
e use o webpack para agrupar todos os arquivos JavaScript em um único arquivo que você pode incorporar ao arquivodist/index.html
:
npm install -g npx npm install --save-dev webpack npx webpack
- Atualize http://localhost:8887/ no navegador e clique em REST > Classificar.
O site exibe 286
como a categoria prevista, que é mapeada para o rótulo Egyptian Cat
no conjunto de dados ImageImage.
8. Conectar o site ao TensorFlow Serving por meio do gRPC
Além do REST, o TensorFlow Serving também é compatível com o gRPC.
O gRPC é um framework de chamada de procedimento remoto (RPC) moderno, de código aberto e de alta performance que pode ser executado em qualquer ambiente. Ele pode conectar serviços de forma eficiente em data centers e com compatibilidade conectável para balanceamento de carga, rastreamento, verificação de integridade e autenticação. Observamos que o gRPC tem uma performance melhor do que o REST na prática.
Enviar solicitações e receber respostas com o gRPC
Há quatro etapas simples:
- Opcional: gerar o código stub do cliente gRPC
- Criar a solicitação gRPC
- Enviar a solicitação gRPC para o TensorFlow Serving
- Extraia o resultado previsto da resposta gRPC e exiba-o na IU.
Realize essas etapas no arquivo src/index.js
.
Opcional: gerar o código stub do cliente gRPC
Para usar o gRPC com o TensorFlow Serving, é necessário seguir o fluxo de trabalho do gRPC. Caso queira saber mais, consulte a documentação do gRPC.
O TensorFlow Serving e o TensorFlow definem os arquivos .proto
automaticamente. No TensorFlow e no TensorFlow Serving 2.8, os seguintes arquivos .proto
são necessários:
tensorflow/core/example/example.proto
tensorflow/core/example/feature.proto
tensorflow/core/protobuf/struct.proto
tensorflow/core/protobuf/saved_object_graph.proto
tensorflow/core/protobuf/saver.proto
tensorflow/core/protobuf/trackable_object_graph.proto
tensorflow/core/protobuf/meta_graph.proto
tensorflow/core/framework/node_def.proto
tensorflow/core/framework/attr_value.proto
tensorflow/core/framework/function.proto
tensorflow/core/framework/types.proto
tensorflow/core/framework/tensor_shape.proto
tensorflow/core/framework/full_type.proto
tensorflow/core/framework/versions.proto
tensorflow/core/framework/op_def.proto
tensorflow/core/framework/graph.proto
tensorflow/core/framework/tensor.proto
tensorflow/core/framework/resource_handle.proto
tensorflow/core/framework/variable.proto
tensorflow_serving/apis/inference.proto
tensorflow_serving/apis/classification.proto
tensorflow_serving/apis/predict.proto
tensorflow_serving/apis/regression.proto
tensorflow_serving/apis/get_model_metadata.proto
tensorflow_serving/apis/input.proto
tensorflow_serving/apis/prediction_service.proto
tensorflow_serving/apis/model.proto
- No terminal, navegue até a pasta
starter/src/proto/
e gere o stub:
bash generate_grpc_stub_js.sh
Criar a solicitação gRPC
Semelhante à solicitação REST, você cria a solicitação gRPC na ramificação gRPC..
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
}
else {
print("Using gRPC")
// TODO: Add code to send a gRPC request to TensorFlow Serving.
}
- Adicione este código à ramificação gRPC:
// Create the gRPC request.
const PredictModule = require('./proto/generated/tensorflow_serving/apis/predict_pb.js');
const PredictionServiceClientModule = require('./proto/generated/tensorflow_serving/apis/prediction_service_grpc_web_pb.js');
const ModelModule = require('./proto/generated/tensorflow_serving/apis/model_pb.js');
const TensorModule = require('./proto/generated/tensorflow/core/framework/tensor_pb.js');
const GPRCURL = 'http://localhost:8080';
const stub = new PredictionServiceClientModule.PredictionServiceClient(GPRCURL);
const modelSpec = new ModelModule.ModelSpec();
modelSpec.setName('inception');
const tensorProto = new TensorModule.TensorProto();
const tensorShapeProto = new TensorModule.TensorShapeProto();
const batchDim = (new TensorModule.TensorShapeProto.Dim()).setSize(1);
const heightDim = (new TensorModule.TensorShapeProto.Dim()).setSize(inputImgHeight);
const widthDim = (new TensorModule.TensorShapeProto.Dim()).setSize(inputImgWidth);
const channelDim = (new TensorModule.TensorShapeProto.Dim()).setSize(3);
tensorShapeProto.setDimList([batchDim, heightDim, widthDim, channelDim]);
tensorProto.setDtype(proto.tensorflow.DataType.DT_FLOAT);
tensorProto.setTensorShape(tensorShapeProto);
context.drawImage(img, 0, 0);
for(let i=0; i<inputImgHeight; i++) {
for (let j=0; j<inputImgWidth; j++) {
tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[0]/255);
tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[1]/255);
tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[2]/255);
}
}
const predictionServiceRequest = new PredictModule.PredictRequest();
predictionServiceRequest.setModelSpec(modelSpec);
predictionServiceRequest.getInputsMap().set('inputs', tensorProto);
Enviar a solicitação gRPC para o TensorFlow Serving
Agora você pode enviar a solicitação.
- Adicione este código imediatamente após o código na ramificação gRPC no snippet de código anterior:
// Send the gRPC request.
stub.predict(predictionServiceRequest, {}, function(err, response) {
// TODO: Add code to process the response.
});
Processar a resposta gRPC do TensorFlow Serving
Por fim, implemente a função de callback acima para processar a resposta.
- Adicione este código ao corpo da função no snippet de código anterior:
// Process the gRPC response.
if (err) {
console.log(err.code);
console.log(err.message);
}
else {
const maxIndex = argmax(response.getOutputsMap().get('logits').getFloatValList());
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
}
Agora, o listener extrai as probabilidades previstas da resposta, identifica a categoria mais provável do objeto e exibe o resultado na IU.
Executar
- No seu terminal, use o webpack para agrupar todos os arquivos JavaScript em um único arquivo que você possa incorporar ao arquivo
index.html
:
npx webpack
- Atualize http://localhost:8887/ no navegador.
- Clique em gRPC > Classificar.
O site exibe a categoria prevista de 286
, que mapeia para o rótulo Egyptian Cat
no conjunto de dados do ImageNet.
9. Parabéns
Você usou o TensorFlow Serving para adicionar recursos de classificação de imagens ao seu site.