1. Avant de commencer
Dans cet atelier de programmation, vous allez apprendre à exécuter une inférence de classification d'images à partir d'un site Web à l'aide de TensorFlow Serving avec REST et gRPC.
Prérequis
- Connaissances de base en développement Web, telles que HTML et JavaScript
- Des connaissances de base en machine learning avec TensorFlow, telles que l'entraînement et le déploiement
- Connaissances de base des terminaux et de Docker
Points abordés
- Découvrez comment trouver des modèles de classification d'images pré-entraînés sur TensorFlow Hub.
- Créer un site Web simple et effectuer des prédictions à l'aide du modèle de classification d'images téléchargé via TensorFlow Serving (REST et gRPC)
- Comment afficher le résultat de la détection dans l'interface utilisateur
Prérequis
- Docker
- Google Chrome
- Serveur Web pour Chrome
- Node.js et npm
- Bash
- Compilateur de tampon de protocole (requis uniquement si vous souhaitez générer à nouveau vous-même le bouchon gRPC)
- Plug-in Générateur de code gRPC-Web (nécessaire uniquement pour générer à nouveau le bouchon gRPC)
2. Configuration
Pour télécharger le code de cet atelier de programmation:
- Accédez à ce dépôt GitHub.
- Cliquez sur Code > Télécharger le fichier ZIP pour télécharger l'ensemble du code de cet atelier de programmation.
- Décompressez le fichier ZIP téléchargé pour décompresser un dossier racine
codelabs
contenant toutes les ressources dont vous avez besoin.
Pour cet atelier de programmation, vous n'avez besoin que des fichiers du sous-répertoire TFServing/ImageClassificationWeb
dans le dépôt, qui contient deux dossiers:
- Le dossier
starter
contient le code de démarrage sur lequel s'appuie cet atelier de programmation. - Le dossier
finished
contient le code final de l'exemple d'application.
3. Installer les dépendances
Pour installer les dépendances:
- Dans votre terminal, accédez au dossier
starter
, puis installez les packages npm requis:
npm install
4. Exécuter le site Web de démarrage
Utilisez Web Server pour Chrome pour charger le fichier TFServing/ImageClassificationWeb/starter/dist/index.html
:
- Saisissez
Chrome://apps/
dans la barre d'adresse de Chrome, puis recherchez Web Server for Chrome (Serveur Web pour Chrome) dans la liste des applications. - Lancez le serveur Web pour Chrome, puis choisissez le dossier
TFServing/ImageClassificationWeb/starter/dist/
. - Cliquez sur le serveur Web pour l'activer, puis accédez à http://localhost:8887/ dans votre navigateur.
Exécuter et explorer le site Web
Le site Web s'affiche. L'interface utilisateur est assez simple: vous devez classer une image de chat et l'utilisateur peut envoyer les données au backend avec REST ou gRPC. Le backend effectue une classification d'image sur l'image et renvoie le résultat de la classification au site Web, qui affiche le résultat.
Si vous cliquez sur Classify, rien ne se passe, car la fonctionnalité ne peut pas encore communiquer avec le backend.
5. Déployer un modèle de classification d'images avec TensorFlow Serving
La classification d'images est une tâche de ML très courante qui classe une image en catégories prédéfinies en fonction du contenu principal de l'image. Voici un exemple de classification des fleurs:
Il existe un certain nombre de modèles de classification d'images pré-entraînés dans TensorFlow Hub. Vous allez utiliser un modèle populaire Inception v3 pour cet atelier de programmation.
Pour déployer le modèle de classification d'images avec TensorFlow Serving, procédez comme suit:
- Téléchargez le fichier de modèle Inception v3.
- Décompressez le fichier
.tar.gz
téléchargé à l'aide d'un outil de décompression, tel que 7-Zip. - Créez un dossier
inception_v3
, puis créez un sous-dossier123
à l'intérieur de celui-ci. - Placez le dossier
variables
et le fichiersaved_model.pb
extraits dans le sous-dossier123
.
Vous pouvez désigner le dossier inception_v3
comme le dossier SavedModel
. 123
est un exemple de numéro de version. Si vous le souhaitez, vous pouvez en choisir un autre.
La structure de dossiers doit se présenter comme suit:
Lancer TensorFlow Serving
- Dans votre terminal, démarrez TensorFlow Serving avec Docker, mais remplacez
PATH/TO/SAVEDMODEL
par le chemin d'accès absolu du dossierinception_v3
sur votre ordinateur.
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 télécharge d'abord l'image TensorFlow Serving, ce qui prend une minute. Ensuite, le service TensorFlow Serving doit commencer. Le journal doit ressembler à l'extrait de code suivant:
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. Configurer le proxy Envoy
Pour le moment, TensorFlow Serving ne définit pas l'en-tête Access-Control-Allow-Origin
. Pour des raisons de sécurité, le navigateur bloque la requête de JavaScript JavaScript sur l'interface. Pour contourner ce problème, vous devez utiliser un proxy, tel qu'Envoy, pour transmettre la requête de JavaScript au backend TensorFlow Serving.
Démarrer Envoy
- Dans votre terminal, téléchargez l'image Envoy et démarrez Envoy avec Docker, mais remplacez l'espace réservé
PATH/TO/ENVOY-CUSTOM.YAML
par le chemin absolu du fichierenvoy-custom.yaml
du dossierstarter
.
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 commence par télécharger automatiquement l'image Envoy. Par la suite, Envoy doit commencer. Le journal doit ressembler à l'extrait de code suivant:
[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. Connecter le site Web à TensorFlow via REST
Le backend est prêt. Vous pouvez donc envoyer des requêtes client à TensorFlow Serving pour classer des images. Il existe deux façons d'envoyer des requêtes à TensorFlow Serving:
- REST
- gRPC
Envoyer des requêtes et recevoir des réponses via REST
Il existe trois étapes simples pour envoyer et recevoir des requêtes via REST:
- Créez la requête REST.
- Envoyez la requête REST à TensorFlow Serving.
- Extrayez le résultat prédit de la réponse REST et affichez le résultat.
Vous devez suivre ces étapes dans le fichier src/index.js
.
Créer la requête REST
Pour le moment, la fonction classify_img()
n'envoie pas la requête REST à TensorFlow Serving. Vous devez d'abord implémenter cette branche REST pour créer une requête REST:
if (radioButtons[0].checked) {
console.log('Using REST');
// TODO: Add code to send a REST request to TensorFlow Serving.
}
TensorFlow Serving attend une requête POST contenant le Tensor d'image pour le modèle Inception v3 que vous utilisez. Vous devez donc extraire les valeurs RVB de chaque pixel de l'image dans un tableau, puis encapsuler le tableau dans un fichier JSON (la charge utile) : de la demande.
- Ajoutez ce code à la branche 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');
}
Envoyer la requête REST à TensorFlow Serving
Vous pouvez à présent envoyer la requête.
- Ajoutez ce code juste après le code ci-dessus dans la branche REST:
// Send the REST request.
xhr.send(data);
Traitez la réponse REST à partir de TensorFlow Serving.
Le modèle Inception v3 renvoie un tableau de probabilités auxquelles l'image appartient à des catégories prédéfinies. Une fois l'opération réussie, vous devriez voir la catégorie la plus probable dans l'interface utilisateur.
Vous mettez en œuvre l'écouteur onload()
pour gérer la réponse.
xhr.onload = () => {
}
- Ajoutez le code suivant à l'écouteur
onload()
:
// Process the REST response.
const response = JSON.parse(xhr.responseText);
const maxIndex = argmax(response['predictions'][0])
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
L'écouteur extrait maintenant les probabilités prédites de la réponse, identifie la catégorie la plus probable de l'objet et affiche le résultat dans l'UI.
Exécuter le code
- Dans votre terminal, accédez au dossier
starter
et utilisez webpack pour regrouper tous les fichiers JavaScript dans un seul fichier que vous pouvez intégrer dans le fichierdist/index.html
:
npm install -g npx npm install --save-dev webpack npx webpack
- Actualisez la page http://localhost:8887/ dans votre navigateur, puis cliquez sur REST > Classifier.
Le site Web affiche 286
comme catégorie prédite, qui correspond au libellé Egyptian Cat
dans l'ensemble de données ImageNet.
8. Connecter le site Web à TensorFlow Serving via gRPC
Outre REST, TensorFlow Serving est également compatible avec gRPC.
gRPC est un framework d'appel de procédure à distance (RPC) moderne et Open Source qui peut être exécuté dans n'importe quel environnement. Grâce à cette solution, les services peuvent être connectés efficacement aux services de centres de données, et ils sont compatibles avec l'équilibrage de charge, le traçage, la vérification de l'état et l'authentification. En pratique, gRPC a été plus performant que REST en pratique.
Envoyer des requêtes et recevoir des réponses avec gRPC
Il existe quatre étapes simples:
- Facultatif: Générez le code du bouchon pour le client gRPC.
- Créez la requête gRPC.
- Envoyez la requête gRPC à TensorFlow Serving.
- Extrayez le résultat prédit de la réponse gRPC et affichez-le dans l'interface utilisateur.
Vous devez suivre la procédure ci-dessous dans le fichier src/index.js
.
Facultatif: Générer le code du bouchon pour le client gRPC
Pour utiliser gRPC avec TensorFlow Serving, vous devez suivre le workflow gRPC. Pour en savoir plus, consultez la documentation gRPC.
TensorFlow Serving et TensorFlow définissent les fichiers .proto
pour vous. À partir de TensorFlow et de TensorFlow Serving 2.8, ces fichiers .proto
sont nécessaires:
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
- Dans votre terminal, accédez au dossier
starter/src/proto/
et générez le bouchon:
bash generate_grpc_stub_js.sh
Créer la requête gRPC
Comme pour la requête REST, vous créez la requête gRPC dans la branche gRPC.
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
}
else {
print("Using gRPC")
// TODO: Add code to send a gRPC request to TensorFlow Serving.
}
- Ajoutez ce code à la branche 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);
Envoyer la requête gRPC à TensorFlow Serving
Vous pouvez à présent envoyer la requête.
- Ajoutez ce code juste après le code de la branche gRPC, dans l'extrait de code précédent:
// Send the gRPC request.
stub.predict(predictionServiceRequest, {}, function(err, response) {
// TODO: Add code to process the response.
});
Traiter la réponse gRPC de TensorFlow Serving
Enfin, vous implémentez la fonction de rappel ci-dessus pour gérer la réponse.
- Ajoutez ce code au corps de la fonction dans l'extrait de code précédent:
// 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;
}
L'écouteur extrait maintenant les probabilités prédites de la réponse, identifie la catégorie la plus probable de l'objet et affiche le résultat dans l'UI.
Exécuter le code
- Dans votre terminal, utilisez webpack pour regrouper tous les fichiers JavaScript dans un seul fichier que vous pouvez intégrer dans le fichier
index.html
:
npx webpack
- Actualisez la page http://localhost:8887/ dans votre navigateur.
- Cliquez sur gRPC > Classifier.
Le site Web affiche la catégorie prédite 286
, qui correspond au libellé Egyptian Cat
de l'ensemble de données ImageNet.
9. Félicitations
Vous avez utilisé TensorFlow Serving pour ajouter des fonctionnalités de classification d'images à votre site Web.