1. Prima di iniziare
In questo codelab, imparerai a eseguire un'inferenza di classificazione delle immagini da un sito web utilizzando TensorFlow Serving con REST e gRPC.
Prerequisiti
- Conoscenza di base dello sviluppo web, come HTML e JavaScript
- Conoscenza di base del machine learning con TensorFlow, ad esempio addestramento e deployment
- Conoscenza di base dei terminali e Docker
Obiettivi didattici
- Come trovare modelli di classificazione delle immagini preaddestrate su TensorFlow Hub.
- Come creare un sito web semplice e fare previsioni con il modello di classificazione delle immagini scaricato tramite TensorFlow Serving (REST e gRPC).
- Come visualizzare il risultato del rilevamento nell'interfaccia utente.
Che cosa ti serve
- Docker
- Google Chrome
- Server web per Chrome
- Node.js e NPM
- Palla
- Compilatore del buffer di protocollo (necessario solo se vuoi rigenerare lo stub gRPC da solo)
- Plug-in Generatore di codice gRPC-web (necessario solo se vuoi rigenerare lo stub gRPC da solo)
2. Configura
Per scaricare il codice per questo codelab:
- Vai a questo repository GitHub.
- Fai clic su Codice > Scarica zip per scaricare tutto il codice di questo codelab.
- Decomprimi il file ZIP scaricato per decomprimere una cartella principale
codelabs
con tutte le risorse necessarie.
Per questo codelab, hai bisogno solo dei file nella sottodirectory TFServing/ImageClassificationWeb
del repository, che contiene due cartelle:
- La cartella
starter
contiene il codice di avvio che crei per questo codelab. - La cartella
finished
contiene il codice completato per l'app di esempio terminata.
3. Installare le dipendenze
Per installare le dipendenze:
- Nel terminale, vai alla cartella
starter
, quindi installa i pacchetti NPM richiesti:
npm install
4. Esegui il sito web iniziale
Utilizza Web Server per Chrome per caricare il file TFServing/ImageClassificationWeb/starter/dist/index.html
:
- Inserisci
Chrome://apps/
nella barra degli indirizzi di Chrome, quindi cerca Web Server per Chrome nell'elenco delle app. - Avvia Web Server per Chrome, quindi scegli la cartella
TFServing/ImageClassificationWeb/starter/dist/
. - Fai clic sul pulsante di attivazione/disattivazione Server web per attivare l'impostazione e vai a http://localhost:8887/ nel browser.
Esegui ed esplora il sito web
Dovresti vedere il sito web ora. L'interfaccia utente è piuttosto semplice: è presente un'immagine gatto in cui vuoi classificare e l'utente può inviare i dati al backend con REST o gRPC. Il backend esegue la classificazione delle immagini sull'immagine e restituisce il risultato della classificazione al sito web, che visualizza il risultato.
Se fai clic su Classificazione, non accade nulla perché non può ancora comunicare con il backend.
5. Esegui il deployment di un modello di classificazione delle immagini con TensorFlow Serving
La classificazione delle immagini è un'attività di machine learning molto comune che classifica un'immagine in categorie predefinite in base ai relativi contenuti principali. Ecco un esempio di classificazione dei fiori:
Su TensorFlow Hub sono disponibili diversi modelli di classificazione delle immagini preaddestrate. Per questo codelab utilizzi un modello molto comune Inception v3.
Per eseguire il deployment del modello di classificazione delle immagini con TensorFlow Serving:
- Scarica il file del modello Inception v3.
- Decomprimi il file
.tar.gz
scaricato con uno strumento di decompressione, ad esempio 7-Zip. - Crea una cartella
inception_v3
, quindi crea una sottocartella123
al suo interno. - Inserisci la cartella
variables
e il filesaved_model.pb
estratti nella sottocartella123
.
Puoi impostare la cartella inception_v3
come cartella SavedModel
. 123
è un numero di versione di esempio. Se vuoi, puoi scegliere un altro numero.
La struttura delle cartelle dovrebbe essere simile a questa:
Avvia la pubblicazione TensorFlow
- Nel terminale, avvia TensorFlow Serving con Docker, ma sostituisci
PATH/TO/SAVEDMODEL
con il percorso assoluto della cartellainception_v3
sul tuo computer.
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
Docker scarica automaticamente prima l'immagine di TensorFlow Serving, che richiede un minuto. In seguito, TensorFlow Serving dovrebbe iniziare. Il log dovrebbe avere il seguente aspetto:
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. Configurare il proxy Envoy
Al momento TensorFlow Serving non imposta l'intestazione Access-Control-Allow-Origin
, quindi il browser blocca la richiesta dal JavaScript frontend a TensorFlow Serving per motivi di sicurezza. Per aggirare questo problema, devi utilizzare un proxy, ad esempio Envoy, per delegare la richiesta da JavaScript al backend TensorFlow Serving.
Avvia Envoy
- Nel tuo terminale, scarica l'immagine Envoy e avvia Envoy con Docker, ma sostituisci il segnaposto
PATH/TO/ENVOY-CUSTOM.YAML
con il percorso assoluto del fileenvoy-custom.yaml
nella cartellastarter
.
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
Docker scarica automaticamente prima l'immagine Envoy. Successivamente, Envoy dovrebbe iniziare. Il log dovrebbe avere il seguente aspetto:
[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. Collega il sito web a TensorFlow tramite REST
Il backend è pronto. Ora puoi inviare richieste client a TensorFlow Serving per classificare le immagini. Esistono due modi per inviare richieste a TensorFlow Serving:
- REST
- gRPC
Inviare richieste e ricevere risposte tramite REST
Esistono tre semplici passaggi per inviare e ricevere richieste tramite REST:
- Crea la richiesta REST.
- Invia la richiesta REST a TensorFlow Serving.
- Estrai il risultato previsto dalla risposta REST e mostralo.
Completa questi passaggi nel file src/index.js
.
Crea la richiesta REST
Attualmente, la funzione classify_img()
non invia la richiesta REST a TensorFlow Serving. Devi implementare questo ramo REST per creare prima una richiesta REST:
if (radioButtons[0].checked) {
console.log('Using REST');
// TODO: Add code to send a REST request to TensorFlow Serving.
}
TensorFlow Serving prevede una richiesta POST che contiene il tensore di immagine per il modello Inception v3 utilizzato, quindi dovrai estrarre i valori RGB da ciascun pixel dell'immagine in un array e quindi aggregare l'array in un JSON, che è il payload della richiesta.
- Aggiungi questo codice al ramo 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');
}
Invia la richiesta REST a TensorFlow Serving
Ora puoi inviare la richiesta.
- Aggiungi questo codice subito dopo il codice riportato sopra nel ramo REST:
// Send the REST request.
xhr.send(data);
Elabora la risposta REST da TensorFlow Serving
Il modello Inception v3 restituisce un array di probabilità che l'immagine appartenga a categorie predefinite. Quando la previsione ha esito positivo, dovresti visualizzare la categoria più probabile nell'interfaccia utente.
Implementi il listener onload()
per gestire la risposta.
xhr.onload = () => {
}
- Aggiungi questo codice al 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;
Ora l'ascoltatore estrae le probabilità previste dalla risposta, identifica la categoria più probabile dell'oggetto e visualizza il risultato nell'interfaccia utente.
Esegui
- Nel terminale, vai alla cartella
starter
e utilizza il pacchetto web per raggruppare tutti i file JavaScript in un unico file che puoi incorporare nel filedist/index.html
:
npm install -g npx npm install --save-dev webpack npx webpack
- Aggiorna http://localhost:8887/ nel browser e fai clic su REST > Classify.
Il sito web mostra 286
come categoria prevista, che è mappata all'etichetta Egyptian Cat
nel set di dati ImageNet.
8. Collega il sito web a TensorFlow Serving tramite gRPC
Oltre a REST, TensorFlow Serving supporta anche gRPC.
gRPC è un framework RPC (Open Procedure Call) moderno e open source eseguibile in qualsiasi ambiente. Può connettere i servizi in modo efficiente all'interno dei diversi data center e fornire un supporto collegabile per bilanciamento del carico, tracciamento, controllo di integrità e autenticazione. È stato osservato che gRPC ha un rendimento migliore rispetto a REST nella pratica.
Inviare richieste e ricevere risposte con gRPC
Sono disponibili quattro semplici passaggi:
- (Facoltativo) Genera il codice stub client gRPC.
- Crea la richiesta gRPC.
- Invia la richiesta gRPC a TensorFlow Serving.
- Estrai il risultato previsto dalla risposta gRPC e lo visualizza nell'interfaccia utente.
Completa questi passaggi nel file src/index.js
.
Facoltativo: genera il codice stub client gRPC
Per utilizzare gRPC con TensorFlow Serving, devi seguire il flusso di lavoro gRPC. Per saperne di più, consulta la documentazione di gRPC.
TensorFlow Serving e TensorFlow definiscono i file .proto
per tuo conto. In data TensorFlow e TensorFlow Serving 2.8, questi file .proto
sono quelli necessari:
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
- Nel terminale, passa alla cartella
starter/src/proto/
e genera lo stub:
bash generate_grpc_stub_js.sh
Crea la richiesta gRPC
Come per la richiesta REST, puoi creare la richiesta gRPC nel ramo gRPC.
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
}
else {
print("Using gRPC")
// TODO: Add code to send a gRPC request to TensorFlow Serving.
}
- Aggiungi questo codice al ramo 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);
Invia la richiesta gRPC a TensorFlow Serving
Ora puoi inviare la richiesta.
- Aggiungi questo codice subito dopo il codice nel ramo gRPC nello snippet di codice precedente:
// Send the gRPC request.
stub.predict(predictionServiceRequest, {}, function(err, response) {
// TODO: Add code to process the response.
});
Elabora la risposta gRPC da TensorFlow Serving
Infine, implementi la funzione di callback descritta sopra per gestire la risposta.
- Aggiungi questo codice al corpo della funzione nello snippet di codice precedente:
// 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;
}
Ora l'ascoltatore estrae le probabilità previste dalla risposta, identifica la categoria più probabile dell'oggetto e visualizza il risultato nell'interfaccia utente.
Esegui
- Nel terminale, utilizza Webpack per raggruppare tutti i file JavaScript in un unico file che puoi incorporare nel file
index.html
:
npx webpack
- Aggiorna http://localhost:8887/ nel browser.
- Fai clic su gRPC > Classify.
Il sito web mostra la categoria prevista di 286
, mappata all'etichetta Egyptian Cat
nel set di dati ImageNet.
9. Complimenti
Hai utilizzato TensorFlow Serving per aggiungere funzionalità di classificazione delle immagini al tuo sito web.