1. Hinweis
In diesem Codelab erfahren Sie, wie Sie mit TensorFlow Serving mit REST und gRPC eine Bildklassifizierungs-Inferenz über eine Website ausführen.
Vorbereitung
- Grundkenntnisse in der Webentwicklung, z. B. HTML und JavaScript
- Grundkenntnisse im Bereich maschinelles Lernen mit TensorFlow, z. B. Training und Bereitstellung
- Grundkenntnisse zu Terminals und Docker
Lerninhalte
- So finden Sie vortrainierte Bildklassifizierungsmodelle in TensorFlow Hub.
- Hier erfahren Sie, wie Sie eine einfache Website erstellen und mit dem heruntergeladenen Bildklassifizierungsmodell über TensorFlow Serving (REST und gRPC) Vorhersagen treffen.
- So rendern Sie das Erkennungsergebnis in der Benutzeroberfläche.
Voraussetzungen
- Docker
- Google Chrome
- Web Server for Chrome
- Node.js und NPM
- Bash
- Protocol Buffer Compiler (nur erforderlich, wenn Sie den gRPC-Stub selbst neu generieren möchten)
- gRPC-Web-Code-Generator-Plugin (nur erforderlich, wenn Sie den gRPC-Stub selbst neu generieren möchten)
2. Einrichten
So laden Sie den Code für dieses Codelab herunter:
- Rufen Sie dieses GitHub-Repository auf.
- Klicken Sie auf Code > Download zip, um den gesamten Code für dieses Codelab herunterzuladen.
- Entpacken Sie die heruntergeladene ZIP-Datei, um einen
codelabs
-Stammordner mit allen benötigten Ressourcen zu entpacken.
Für dieses Codelab benötigen Sie nur die Dateien im Unterverzeichnis TFServing/ImageClassificationWeb
im Repository, das zwei Ordner enthält:
- Der Ordner
starter
enthält den Startcode, auf dem Sie in diesem Codelab aufbauen. - Der Ordner
finished
enthält den vollständigen Code der fertigen Beispiel-App.
3. Abhängigkeiten installieren
So installieren Sie die Abhängigkeiten:
- Wechseln Sie im Terminal zum Ordner
starter
und installieren Sie dann die erforderlichen NPM-Pakete:
npm install
4. Starter-Website ausführen
So laden Sie die TFServing/ImageClassificationWeb/starter/dist/index.html
-Datei mit Web Server for Chrome:
- Geben Sie
Chrome://apps/
in die Adressleiste von Chrome ein und suchen Sie in der App-Liste nach Web Server for Chrome. - Starten Sie Web Server for Chrome und wählen Sie den Ordner
TFServing/ImageClassificationWeb/starter/dist/
aus. - Klicken Sie auf den Schalter Web Server, um ihn zu aktivieren, und rufen Sie dann in Ihrem Browser http://localhost:8887/ auf.
Website ausführen und erkunden
Jetzt sollte die Website angezeigt werden. Die Benutzeroberfläche ist recht einfach: Es gibt ein Katzenbild, das Sie klassifizieren möchten, und der Nutzer kann die Daten über REST oder gRPC an das Backend senden. Das Backend führt eine Bildklassifizierung für das Bild durch und gibt das Klassifizierungsergebnis an die Website zurück, die das Ergebnis anzeigt.
Wenn Sie auf Classify (Klassifizieren) klicken, passiert nichts, da noch keine Kommunikation mit dem Backend möglich ist.
5. Bildklassifizierungsmodell mit TensorFlow Serving bereitstellen
Die Bildklassifizierung ist eine sehr häufige ML-Aufgabe, bei der ein Bild anhand des primären Inhalts des Bildes in vordefinierte Kategorien eingeteilt wird. Hier ist ein Beispiel für die Klassifizierung von Blumen:
Auf TensorFlow Hub sind eine Reihe vortrainierter Bildklassifizierungsmodelle verfügbar. In diesem Codelab verwenden Sie das beliebte Modell Inception v3.
So stellen Sie das Bildklassifizierungsmodell mit TensorFlow Serving bereit:
- Laden Sie die Modelldatei Inception v3 herunter.
- Entpacken Sie die heruntergeladene Datei
.tar.gz
mit einem Dekomprimierungstool wie 7‑Zip. - Erstellen Sie einen
inception_v3
-Ordner und darin einen123
-Unterordner. - Verschieben Sie den extrahierten Ordner
variables
und die Dateisaved_model.pb
in den Unterordner123
.
Sie können auf den Ordner inception_v3
als Ordner SavedModel
verweisen. 123
ist eine Beispielversionsnummer. Sie können auch eine andere Nummer auswählen.
Die Ordnerstruktur sollte so aussehen:
TensorFlow Serving starten
- Starten Sie TensorFlow Serving mit Docker in Ihrem Terminal, ersetzen Sie dabei
PATH/TO/SAVEDMODEL
durch den absoluten Pfad des Ordnersinception_v3
auf Ihrem 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 lädt zuerst automatisch das TensorFlow Serving-Image herunter. Das dauert etwa eine Minute. Danach sollte TensorFlow Serving gestartet werden. Das Log sollte so aussehen:
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. Envoy-Proxy einrichten
Derzeit wird der Access-Control-Allow-Origin
-Header von TensorFlow Serving nicht festgelegt. Daher blockiert der Browser die Anfrage vom Frontend-JavaScript an TensorFlow Serving aus Sicherheitsgründen. Um dieses Problem zu umgehen, müssen Sie einen Proxy wie Envoy verwenden, um die Anfrage von JavaScript an das TensorFlow Serving-Backend weiterzuleiten.
Envoy starten
- Laden Sie in Ihrem Terminal das Envoy-Image herunter und starten Sie Envoy mit Docker. Ersetzen Sie dabei den Platzhalter
PATH/TO/ENVOY-CUSTOM.YAML
durch den absoluten Pfad der Dateienvoy-custom.yaml
im Ordnerstarter
.
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 lädt das Envoy-Image automatisch herunter. Danach sollte Envoy starten. Das Log sollte so aussehen:
[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. Website über REST mit TensorFlow verbinden
Das Backend ist jetzt bereit. Sie können Clientanfragen an TensorFlow Serving senden, um Bilder zu klassifizieren. Es gibt zwei Möglichkeiten, Anfragen an TensorFlow Serving zu senden:
- REST
- gRPC
Anfragen über REST senden und Antworten empfangen
Es gibt drei einfache Schritte zum Senden und Empfangen von Anfragen über REST:
- Erstellen Sie die REST-Anfrage.
- Senden Sie die REST-Anfrage an TensorFlow Serving.
- Extrahieren Sie das vorhergesagte Ergebnis aus der REST-Antwort und zeigen Sie es an.
Diese Schritte werden in der Datei src/index.js
ausgeführt.
REST-Anfrage erstellen
Derzeit wird die REST-Anfrage nicht über die Funktion classify_img()
an TensorFlow Serving gesendet. Sie müssen diesen REST-Zweig implementieren, um zuerst eine REST-Anfrage zu erstellen:
if (radioButtons[0].checked) {
console.log('Using REST');
// TODO: Add code to send a REST request to TensorFlow Serving.
}
TensorFlow Serving erwartet eine POST-Anfrage, die den Bildtensor für das verwendete Inception v3-Modell enthält. Sie müssen also die RGB-Werte aus jedem Pixel des Bildes in ein Array extrahieren und das Array dann in ein JSON-Objekt einbetten, das die Nutzlast der Anfrage darstellt.
- Fügen Sie diesen Code in den REST-Zweig ein:
//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');
}
REST-Anfrage an TensorFlow Serving senden
Jetzt können Sie die Anfrage senden.
- Fügen Sie diesen Code direkt nach dem obigen Code im REST-Zweig ein:
// Send the REST request.
xhr.send(data);
REST-Antwort von TensorFlow Serving verarbeiten
Das Inception v3-Modell gibt ein Array von Wahrscheinlichkeiten zurück, dass das Bild zu vordefinierten Kategorien gehört. Wenn die Vorhersage erfolgreich ist, sollten Sie die wahrscheinlichste Kategorie auf der Benutzeroberfläche ausgeben.
Sie implementieren den onload()
-Listener, um die Antwort zu verarbeiten.
xhr.onload = () => {
}
- Fügen Sie diesen Code dem
onload()
-Listener hinzu:
// Process the REST response.
const response = JSON.parse(xhr.responseText);
const maxIndex = argmax(response['predictions'][0])
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
Der Listener extrahiert nun die vorhergesagten Wahrscheinlichkeiten aus der Antwort, ermittelt die wahrscheinlichste Kategorie des Objekts und zeigt das Ergebnis auf der Benutzeroberfläche an.
Ausführen
- Wechseln Sie im Terminal zum Ordner
starter
und verwenden Sie webpack, um alle JavaScript-Dateien in einer einzigen Datei zu bündeln, die Sie in die Dateidist/index.html
einbetten können:
npm install -g npx npm install --save-dev webpack npx webpack
- Aktualisieren Sie http://localhost:8887/ in Ihrem Browser und klicken Sie dann auf REST > Classify.
Auf der Website wird 286
als vorhergesagte Kategorie angezeigt, die dem Label Egyptian Cat
im ImageNet-Dataset entspricht.
8. Website über gRPC mit TensorFlow Serving verbinden
Neben REST unterstützt TensorFlow Serving auch gRPC.
gRPC ist ein modernes Open-Source-Framework für hochleistungsfähige Remote-Prozeduraufrufe (RPCs), das in jeder Umgebung ausgeführt werden kann. Damit lassen sich Dienste in und zwischen Rechenzentren effizient verbinden. Außerdem bietet es Plug-in-Unterstützung für Load-Balancing, Tracing, Systemdiagnosen und Authentifizierung. In der Praxis hat sich gezeigt, dass gRPC leistungsfähiger als REST ist.
Anfragen senden und Antworten mit gRPC empfangen
Dazu sind nur vier einfache Schritte erforderlich:
- Optional: Generieren Sie den gRPC-Client-Stub-Code.
- Erstellen Sie die gRPC-Anfrage.
- Senden Sie die gRPC-Anfrage an TensorFlow Serving.
- Extrahieren Sie das vorhergesagte Ergebnis aus der gRPC-Antwort und zeigen Sie es in der Benutzeroberfläche an.
Sie führen diese Schritte in der Datei src/index.js
aus.
Optional: gRPC-Client-Stubcode generieren
Wenn Sie gRPC mit TensorFlow Serving verwenden möchten, müssen Sie dem gRPC-Workflow folgen. Weitere Informationen finden Sie in der gRPC-Dokumentation.
TensorFlow Serving und TensorFlow definieren die .proto
-Dateien für Sie. Ab TensorFlow und TensorFlow Serving 2.8 sind die folgenden .proto
-Dateien erforderlich:
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
- Wechseln Sie in Ihrem Terminal zum Ordner
starter/src/proto/
und generieren Sie den Stub:
bash generate_grpc_stub_js.sh
gRPC-Anfrage erstellen
Ähnlich wie bei der REST-Anfrage erstellen Sie die gRPC-Anfrage im gRPC-Zweig.
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
}
else {
print("Using gRPC")
// TODO: Add code to send a gRPC request to TensorFlow Serving.
}
- Fügen Sie diesen Code dem gRPC-Branch hinzu:
// 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);
gRPC-Anfrage an TensorFlow Serving senden
Jetzt können Sie die Anfrage senden.
- Fügen Sie diesen Code direkt nach dem Code im gRPC-Zweig im vorherigen Code-Snippet ein:
// Send the gRPC request.
stub.predict(predictionServiceRequest, {}, function(err, response) {
// TODO: Add code to process the response.
});
gRPC-Antwort von TensorFlow Serving verarbeiten
Schließlich implementieren Sie die Callback-Funktion oben, um die Antwort zu verarbeiten.
- Fügen Sie diesen Code in den Funktionskörper im vorherigen Code-Snippet ein:
// 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;
}
Der Listener extrahiert nun die vorhergesagten Wahrscheinlichkeiten aus der Antwort, ermittelt die wahrscheinlichste Kategorie des Objekts und zeigt das Ergebnis auf der Benutzeroberfläche an.
Ausführen
- Verwenden Sie in Ihrem Terminal webpack, um alle JavaScript-Dateien in einer einzigen Datei zu bündeln, die Sie in die
index.html
-Datei einbetten können:
npx webpack
- Aktualisieren Sie http://localhost:8887/ in Ihrem Browser.
- Klicken Sie auf gRPC > Classify (gRPC > Klassifizieren).
Auf der Website wird die vorhergesagte Kategorie von 286
angezeigt, die dem Label Egyptian Cat
im ImageNet-Dataset entspricht.
9. Glückwunsch
Sie haben TensorFlow Serving verwendet, um Ihrer Website Funktionen zur Bildklassifizierung hinzuzufügen.