1. Hinweis
In diesem Codelab lernen Sie, wie Sie mit TensorFlow Serving mit REST und gRPC eine Bildklassifizierungs-Inferenz von einer Website ausführen.
Vorbereitung
- Grundkenntnisse in der Webentwicklung wie HTML und JavaScript
- Grundkenntnisse in Machine Learning mit TensorFlow, z. B. für Training und Bereitstellung
- Grundkenntnisse zu Terminals und Docker
Lerninhalte
- Vortrainierte Modelle zur Bildklassifizierung in TensorFlow Hub finden
- Anleitung zum Erstellen einer einfachen Website und zum Erstellen von Vorhersagen mit dem heruntergeladenen Bildklassifizierungsmodell über TensorFlow Serving (REST und gRPC).
- Rendering des Erkennungsergebnisses in der UI
Voraussetzungen
- Docker
- Google Chrome
- Webserver für Chrome
- Node.js und NPM
- Bash
- Protokollpuffer-Compiler (nur erforderlich, wenn Sie den gRPC-Stub selbst noch einmal generieren möchten)
- gRPC-Webcode-Generator-Plug-in (nur erforderlich, wenn Sie den gRPC-Stub selbst noch einmal 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 gt; ZIP herunterladen, um den gesamten Code für dieses Codelab herunterzuladen.
- Entpacken Sie die heruntergeladene ZIP-Datei, um den Root-Ordner
codelabs
mit allen benötigten Ressourcen zu entpacken.
Für dieses Codelab benötigen Sie nur die Dateien im Unterverzeichnis TFServing/ImageClassificationWeb
des Repositorys mit zwei Ordnern:
- Der Ordner
starter
enthält den Startcode, den Sie für dieses Codelab erstellen. - Der Ordner
finished
enthält den abgeschlossenen Code für die fertige Beispiel-App.
3. Abhängigkeiten installieren
So installieren Sie die Abhängigkeiten:
- Gehen Sie in Ihrem Terminal zum Ordner
starter
und installieren Sie die erforderlichen NPM-Pakete:
npm install
4. Starter-Website ausführen
Verwende Web Server für Chrome, um die Datei TFServing/ImageClassificationWeb/starter/dist/index.html
zu laden:
- Geben Sie in die Adressleiste von Chrome
Chrome://apps/
ein und suchen Sie in der App-Liste nach Web Server for Chrome. - Starten Sie Webserver für Chrome und wählen Sie anschließend den Ordner
TFServing/ImageClassificationWeb/starter/dist/
aus. - Klicken Sie auf den Schalter Webserver, um ihn zu aktivieren, und gehen Sie dann in Ihrem Browser zu http://localhost:8887/.
Die Website entdecken und erkunden
Du solltest die Website jetzt sehen. Die Benutzeroberfläche ist ziemlich unkompliziert: Es gibt ein Katzenbild, das Sie klassifizieren möchten und der Nutzer die Daten mit REST oder gRPC an das Back-End senden können. Das Back-End führt eine Bildklassifizierung für das Bild durch und gibt das Klassifizierungsergebnis an die Website zurück, auf der das Ergebnis angezeigt wird.
Wenn Sie auf Klassifizieren klicken, passiert nichts, da die Kommunikation mit dem Back-End noch nicht möglich ist.
5. Bildklassifizierungsmodell mit TensorFlow Serving bereitstellen
Die Bildklassifizierung ist eine sehr gängige ML-Aufgabe, die ein Bild basierend auf dem Hauptinhalt des Bildes in vordefinierte Kategorien einteilt. Hier ein Beispiel für die Klassifizierung von Blumen:
Auf TensorFlow Hub gibt es eine Reihe von vortrainierten Modellen zur Bildklassifizierung. Für dieses Codelab verwenden Sie ein beliebtes Inception v3-Modell.
So stellen Sie das Bildklassifizierungsmodell mit TensorFlow Serving bereit:
- Laden Sie die Modelldatei Inception v3 herunter.
- Entpacken Sie die heruntergeladene
.tar.gz
-Datei mit einem Dekomprimierungstool, z. B. 7-Zip. - Erstellen Sie einen Ordner
inception_v3
und erstellen Sie darin einen Unterordner123
. - Verschieben Sie den extrahierten Ordner
variables
und die Dateisaved_model.pb
in den Unterordner123
.
Du kannst den Ordner inception_v3
als Ordner SavedModel
verwenden. 123
ist eine Beispielversionsnummer. Wenn du möchtest, kannst du eine andere Nummer auswählen.
Die Ordnerstruktur sollte so aussehen:
TensorFlow-Bereitstellung starten
- Starten Sie in Ihrem Terminal TensorFlow Serving mit Docker, ersetzen Sie jedoch
PATH/TO/SAVEDMODEL
durch den absoluten Pfad desinception_v3
-Ordners 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 automatisch das TensorFlow Serving-Image herunter. Das dauert eine Minute. Danach sollte die TensorFlow-Bereitstellung beginnen. Das Protokoll 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 stellt TensorFlow Serving den Access-Control-Allow-Origin
-Header nicht fest, sodass der Browser die Anfrage aus Sicherheitsgründen vom Front-End-JavaScript an TensorFlow Serving blockiert. Sie können das Problem umgehen, indem Sie einen Proxy wie Envoy verwenden, um die JavaScript-Anfrage an das TensorFlow Serving-Back-End zu senden.
Envoy starten
- Laden Sie auf Ihrem Terminal das Envoy-Image herunter und starten Sie Envoy mit Docker. Ersetzen Sie jedoch 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 automatisch das Envoy-Image herunter. Anschließend sollte Envoy starten. Das Protokoll 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 TensorFlow mit TensorFlow verbinden
Das Back-End ist jetzt bereit, damit Sie Clientanfragen zur Klassifizierung von Bildern an TensorFlow Serving senden können. Es gibt zwei Möglichkeiten, Anfragen an die TensorFlow-Bereitstellung zu senden:
- REST
- gRPC
Anfragen senden und Antworten über REST erhalten
Das Senden und Empfangen von Anfragen über REST umfasst drei einfache Schritte:
- 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 das Ergebnis an.
Diese Schritte führen Sie in der Datei src/index.js
aus.
REST-Anfrage erstellen
Aktuell sendet die Funktion classify_img()
die REST-Anfrage nicht an TensorFlow Serving. Sie müssen diesen REST-Branch implementieren, um eine REST-Anfrage erstellen zu können:
if (radioButtons[0].checked) {
console.log('Using REST');
// TODO: Add code to send a REST request to TensorFlow Serving.
}
Für TensorFlow Serving wird eine POST-Anfrage erwartet, die den Bildtensor für das von Ihnen verwendete Inception v3-Modell enthält. Daher müssen Sie die RGB-Werte aus jedem Pixel des Bildes in einem Array extrahieren und das Array in einer JSON-Datei zusammenfassen (die Nutzlast der Anfrage).
- Fügen Sie diesen Code dem REST-Branch hinzu:
//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
Sie können die Anfrage jetzt senden.
- Fügen Sie diesen Code direkt nach dem obigen Code im REST-Branch hinzu:
// Send the REST request.
xhr.send(data);
REST-Antwort von TensorFlow Serving verarbeiten
Das Inception v3-Modell gibt ein Array mit Wahrscheinlichkeiten zurück, dass das Bild zu vordefinierten Kategorien gehört. Wenn die Vorhersage erfolgreich ist, sollten Sie die Kategorie mit der höchsten Wahrscheinlichkeit auf der Benutzeroberfläche ausgeben.
Du implementierst den onload()
-Listener für die Antwort.
xhr.onload = () => {
}
- Füge 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 vorhergesagte Wahrscheinlichkeiten aus der Antwort, identifiziert die wahrscheinlichste Kategorie des Objekts und zeigt das Ergebnis in der Benutzeroberfläche an.
Ausführen
- Wechseln Sie in Ihrem 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 prognostizierte Kategorie angezeigt. Diese ist dem Label Egyptian Cat
im ImageNet-Dataset zugeordnet.
8. Website mit TensorFlow über gRPC verbinden
Zusätzlich zur REST-Unterstützung unterstützt TensorFlow Serving auch BeyondCorp.
gRPC ist ein modernes, leistungsstarkes Open-Source-RPC-Framework (Remote Procedure Call), das in jeder Umgebung ausgeführt werden kann. Dank der flexiblen Unterstützung für Load-Balancing, Tracing, Systemdiagnose und Authentifizierung lassen sich Dienste effizient in und zwischen Rechenzentren verbinden. Es wurde festgestellt, dass gRPC in der Praxis leistungsstärker ist als REST.
Anfragen mit gRPC senden und Antworten erhalten
Es sind vier einfache Schritte:
- Optional: Erstellen 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 UI an.
Sie führen die Schritte in der Datei src/index.js
aus.
Optional: gRPC-Client-Stub-Code generieren
Wenn Sie gRPC mit TensorFlow Serving verwenden möchten, müssen Sie dem gRPC-Workflow folgen. Weitere Informationen finden Sie in der BeyondCorp-Dokumentation.
Die Bereitstellung von .proto
-Dateien wird durch TensorFlow Serving und TensorFlow definiert. Ab TensorFlow und TensorFlow Serving 2.8 sind diese .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
- Gehen Sie in Ihrem Terminal zum Ordner
starter/src/proto/
und generieren Sie die Stubs:
bash generate_grpc_stub_js.sh
gRPC-Anfrage erstellen
Ähnlich wie bei der REST-Anfrage erstellen Sie die gRPC-Anfrage im gRPC-Branch.
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
Sie können die Anfrage jetzt senden.
- Fügen Sie diesen Code direkt nach dem Code im gRPC-Branch 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
Zum Abschluss implementieren Sie die obige Callback-Funktion, um die Antwort zu verarbeiten.
- Fügen Sie folgenden Code in den Funktionstext 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 vorhergesagte Wahrscheinlichkeiten aus der Antwort, identifiziert die wahrscheinlichste Kategorie des Objekts und zeigt das Ergebnis in der Benutzeroberfläche an.
Ausführen
- Verwende im Terminal mit Webpack alle JavaScript-Dateien in einer einzigen Datei, die du in die Datei
index.html
einbetten kannst:
npx webpack
- Aktualisieren Sie http://localhost:8887/ in Ihrem Browser.
- Klicken Sie auf GRP > Classify.
Auf der Website wird die prognostizierte Kategorie von 286
angezeigt. Diese ist dem Label Egyptian Cat
im ImageNet-Dataset zugeordnet.
9. Glückwunsch
Sie haben TensorFlow Serving genutzt, um Ihre Website mit Klassifizierungsfunktionen für Bilder zu versehen.